//----------------------------------------------------------------------------- // V12 Engine // // Copyright (c) 2001 GarageGames.Com // Portions Copyright (c) 2001 by Sierra Online, Inc. //----------------------------------------------------------------------------- #include "console/consoleTypes.h" #include "console/console.h" #include "dgl/dgl.h" #include "gui/guiTypes.h" #include "gui/guiTextCtrl.h" #include "gui/guiTextEditCtrl.h" #include "gui/guiInspector.h" #include "gui/guiCheckBoxCtrl.h" #include "gui/guiPopUpCtrl.h" #include "platform/event.h" // datablocks #include "game/gameBase.h" #include "game/explosion.h" #include "game/particleEngine.h" #include "game/projectile.h" #include "sim/cannedChatDataBlock.h" #include "game/Debris.h" #include "game/commanderMapIcon.h" #include "game/shockwave.h" #include "game/splash.h" #include "game/shieldImpact.h" #include "game/projEnergy.h" #include "game/projBomb.h" #define NULL_STRING "" GuiInspector::GuiInspector() { mEditControlOffset = 5; mEntryHeight = 16; mTextExtent = 80; mEntrySpacing = 2; mMaxMenuExtent = 80; } void GuiInspector::onRemove() { mTarget = 0; while(size()) first()->deleteObject(); Parent::onRemove(); } //------------------------------------------------------------------------------ static S32 QSORT_CALLBACK stringCompare(const void *a,const void *b) { StringTableEntry sa = *(StringTableEntry*)a; StringTableEntry sb = *(StringTableEntry*)b; return(dStricmp(sa, sb)); } void GuiInspector::inspect(SimObject * obj) { mTarget = obj; while(size()) first()->deleteObject(); S32 curYOffset = mEntrySpacing; if(!bool(mTarget)) { resize(mBounds.point, Point2I(mBounds.extent.x, curYOffset)); return; } // add in the static fields AbstractClassRep::FieldList fieldList = mTarget->getFieldList(); AbstractClassRep::FieldList::iterator itr; for(itr = fieldList.begin(); itr != fieldList.end(); itr++) { if(itr->type == AbstractClassRep::DepricatedFieldType) continue; char fdata[1024]; const char * dstr = Con::getData(itr->type, (void *)(S32(obj) + itr->offset), 0, itr->table, itr->flag); if(!dstr) dstr = ""; expandEscape(fdata, dstr); GuiTextCtrl * textCtrl = new GuiTextCtrl(); textCtrl->setField("profile", "GuiTextProfile"); textCtrl->setField("text", itr->pFieldname); textCtrl->registerObject(); addObject(textCtrl); S32 textWidth = textCtrl->mProfile->mFont->getStrWidth(itr->pFieldname); S32 xStartPoint = (textWidth < (mTextExtent + mEntrySpacing + mEditControlOffset)) ? (mEntrySpacing + mTextExtent) : textWidth + mEditControlOffset; textCtrl->mBounds.point = Point2I(mEntrySpacing, curYOffset); textCtrl->mBounds.extent = Point2I(textWidth, mEntryHeight); S32 maxWidth = mBounds.extent.x - xStartPoint - mEntrySpacing; //now add the field GuiControl * editControl = NULL; switch(itr->type) { // text control default: { GuiTextEditCtrl * edit = new GuiTextEditCtrl(); edit->setField("profile", "GuiInspectorTextEditProfile"); edit->setField("text", fdata); editControl = edit; edit->mBounds.point = Point2I(xStartPoint, curYOffset); edit->mBounds.extent = Point2I(maxWidth, mEntryHeight); edit->setSizing(GuiControl::horizResizeWidth, GuiControl::vertResizeBottom); break; } // checkbox case TypeBool: case TypeFlag: { GuiCheckBoxCtrl * checkBox = new GuiCheckBoxCtrl(); checkBox->setField("profile", "GuiCheckBoxProfile"); checkBox->mBounds.point = Point2I(xStartPoint, curYOffset); checkBox->mBounds.extent = Point2I(mEntryHeight, mEntryHeight); checkBox->setScriptValue(fdata); editControl = checkBox; break; } // dropdown list case TypeEnum: { AssertFatal(itr->table, "TypeEnum declared with NULL table"); GuiPopUpMenuCtrl * menu = new GuiPopUpMenuCtrl(); menu->setField("profile", "GuiPopUpMenuProfile"); menu->setField("text", fdata); menu->mBounds.point = Point2I(xStartPoint, curYOffset); menu->mBounds.extent = Point2I(getMin(maxWidth, mMaxMenuExtent), mEntryHeight); //now add the entries for(S32 i = 0; i < itr->table->size; i++) menu->addEntry(itr->table->table[i].label, itr->table->table[i].index); editControl = menu; break; } // guiprofiles case TypeGuiProfile: { GuiPopUpMenuCtrl * menu = new GuiPopUpMenuCtrl(); menu->setField("profile", "GuiPopUpMenuProfile"); menu->setField("text", *fdata ? fdata : NULL_STRING); menu->mBounds.point = Point2I(xStartPoint, curYOffset); menu->mBounds.extent = Point2I(getMin(maxWidth, mMaxMenuExtent), mEntryHeight); // add 'NULL' menu->addEntry(NULL_STRING, -1); // add entries to list so they can be sorted prior to adding to menu (want null on top) Vector entries; SimGroup * grp = Sim::getGuiDataGroup(); for(SimGroup::iterator i = grp->begin(); i != grp->end(); i++) { GuiControlProfile * profile = dynamic_cast(*i); if(profile) entries.push_back(profile->getName()); } // sort the entries dQsort(entries.address(), entries.size(), sizeof(StringTableEntry), stringCompare); for(U32 j = 0; j < entries.size(); j++) menu->addEntry(entries[j], 0); editControl = menu; break; } // datablock types case TypeGameBaseDataPtr: case TypeExplosionDataPtr: case TypeShockwaveDataPtr: case TypeSplashDataPtr: case TypeEnergyProjectileDataPtr: case TypeBombProjectileDataPtr: case TypeParticleEmitterDataPtr: case TypeAudioDescriptionPtr: case TypeAudioProfilePtr: case TypeProjectileDataPtr: case TypeCannedChatItemPtr: case TypeDebrisDataPtr: case TypeCommanderIconDataPtr: { GuiPopUpMenuCtrl * menu = new GuiPopUpMenuCtrl(); menu->setField("profile", "GuiPopUpMenuProfile"); menu->setField("text", *fdata ? fdata : NULL_STRING); menu->mBounds.point = Point2I(xStartPoint, curYOffset); menu->mBounds.extent = Point2I(getMin(maxWidth, mMaxMenuExtent), mEntryHeight); // add the 'NULL' entry on top menu->addEntry(NULL_STRING, -1); // add to a list so they can be sorted Vector entries; SimGroup * grp = Sim::getDataBlockGroup(); for(SimGroup::iterator i = grp->begin(); i != grp->end(); i++) { SimObject * obj = 0; switch(itr->type) { case TypeGameBaseDataPtr: obj = dynamic_cast(*i); break; case TypeExplosionDataPtr: obj = dynamic_cast(*i); break; case TypeShockwaveDataPtr: obj = dynamic_cast(*i); break; case TypeSplashDataPtr: obj = dynamic_cast(*i); break; case TypeEnergyProjectileDataPtr: obj = dynamic_cast(*i); break; case TypeBombProjectileDataPtr: obj = dynamic_cast(*i); break; case TypeParticleEmitterDataPtr: obj = dynamic_cast(*i); break; case TypeAudioDescriptionPtr: obj = dynamic_cast(*i); break; case TypeAudioProfilePtr: obj = dynamic_cast(*i); break; case TypeProjectileDataPtr: obj = dynamic_cast(*i); break; case TypeCannedChatItemPtr: obj = dynamic_cast(*i); break; case TypeDebrisDataPtr: obj = dynamic_cast(*i); break; case TypeCommanderIconDataPtr: obj = dynamic_cast(*i); break; } if(obj) entries.push_back(obj->getName()); } // sort the entries dQsort(entries.address(), entries.size(), sizeof(StringTableEntry), stringCompare); for(U32 j = 0; j < entries.size(); j++) menu->addEntry(entries[j], 0); editControl = menu; break; } } if(editControl) { char buf[256]; dSprintf(buf, sizeof(buf), "InspectStatic%s", itr->pFieldname); editControl->registerObject(buf); addObject(editControl); } curYOffset += (mEntryHeight + mEntrySpacing); } // dynamic field seperator: text GuiTextCtrl * textCtrl = new GuiTextCtrl(); textCtrl->setField("profile", "GuiTextProfile"); textCtrl->setField("text", " Dynamic Fields"); textCtrl->registerObject(); textCtrl->mBounds.point = Point2I(mEntrySpacing, curYOffset); textCtrl->mBounds.extent = Point2I(mTextExtent, mEntryHeight); addObject(textCtrl); // button GuiButtonCtrl * button = new GuiButtonCtrl(); button->setField("profile", "GuiButtonProfile"); button->setField("text", "Add"); Con::setIntVariable("InspectingObject", mTarget->getId()); Con::setIntVariable("Inspector", getId()); S32 textWidth = textCtrl->mProfile->mFont->getStrWidth(textCtrl->getScriptValue()); S32 xStartPoint = (textWidth < (mTextExtent + mEntrySpacing + mEditControlOffset)) ? (mEntrySpacing + mTextExtent) : textWidth + mEditControlOffset; S32 maxWidth = mBounds.extent.x - xStartPoint - mEntrySpacing; button->mBounds.point = Point2I(xStartPoint, curYOffset); button->mBounds.extent = Point2I(getMin(maxWidth, mMaxMenuExtent), mEntryHeight); button->registerObject(); char buf[1024]; dSprintf(buf, sizeof(buf), "%d.addDynamicField(%d);", getId(), mTarget->getId()); button->setField("command", buf); addObject(button); // offset curYOffset += (mEntryHeight + mEntrySpacing); // add the dynamic fields SimFieldDictionary * fieldDictionary = mTarget->getFieldDictionary(); for(SimFieldDictionaryIterator ditr(fieldDictionary); *ditr; ++ditr) { SimFieldDictionary::Entry * entry = (*ditr); // create the name ctrl GuiTextCtrl * nameCtrl = new GuiTextCtrl(); nameCtrl->setField("profile", "GuiTextProfile"); nameCtrl->setField("text", entry->slotName); nameCtrl->registerObject(); addObject(nameCtrl); nameCtrl->mBounds.point = Point2I(mEntrySpacing, curYOffset); nameCtrl->mBounds.extent = Point2I(mTextExtent, mEntryHeight); // add a 'remove' button GuiButtonCtrl * button = new GuiButtonCtrl(); button->setField("profile", "GuiButtonProfile"); button->setField("text", "x"); button->registerObject(); addObject(button); char buf[1024]; dSprintf(buf, sizeof(buf), "%d.%s = \"\";%d.inspect(%d);", mTarget->getId(), entry->slotName, getId(), mTarget->getId()); button->setField("command", buf); S32 textWidth = mProfile->mFont->getStrWidth(entry->slotName); // 10x10 with 2x2 frame button->mBounds.point.set(textWidth + 4, curYOffset + 2); button->mBounds.extent.set(10, 10); textWidth += 14; S32 xStartPoint = (textWidth < (mTextExtent + mEntrySpacing + mEditControlOffset)) ? (mEntrySpacing + mTextExtent) : textWidth + mEditControlOffset; S32 maxWidth = mBounds.extent.x - xStartPoint - mEntrySpacing; expandEscape(buf, entry->value ? entry->value : ""); // create the edit ctrl GuiTextEditCtrl * editCtrl = new GuiTextEditCtrl(); editCtrl->setField("profile", "GuiInspectorTextEditProfile"); editCtrl->setField("text", buf); editCtrl->setSizing(GuiControl::horizResizeWidth, GuiControl::vertResizeBottom); dSprintf(buf, sizeof(buf), "InspectDynamic%s", entry->slotName); editCtrl->registerObject(buf); addObject(editCtrl); editCtrl->mBounds.point = Point2I(xStartPoint, curYOffset); editCtrl->mBounds.extent = Point2I(maxWidth, mEntryHeight); curYOffset += (mEntryHeight + mEntrySpacing); } resize(mBounds.point, Point2I(mBounds.extent.x, curYOffset)); } void GuiInspector::apply(const char * newName) { if(!bool(mTarget)) { while(size()) first()->deleteObject(); return; } mTarget->assignName(newName); mTarget->inspectPreApply(); SimObject * obj = static_cast(mTarget); //now add in the fields AbstractClassRep::FieldList fieldList = mTarget->getFieldList(); AbstractClassRep::FieldList::iterator itr; for(itr = fieldList.begin(); itr != fieldList.end(); itr++) { if(itr->type == AbstractClassRep::DepricatedFieldType) continue; char fdata[1024]; dSprintf(fdata, sizeof(fdata), "InspectStatic%s", itr->pFieldname); GuiControl * editCtrl = NULL; SimObject * inspectObj = Sim::findObject(fdata); if(inspectObj) editCtrl = dynamic_cast(inspectObj); if(!editCtrl) continue; const char * newValue = 0; // check for null on profiles (-1 popup id) GuiPopUpMenuCtrl * menu = dynamic_cast(editCtrl); if(!(menu && (menu->getSelected() == -1))) newValue = editCtrl->getScriptValue(); if(!newValue) newValue = ""; dStrcpy(fdata, newValue); collapseEscape(fdata); //now set the field const char *argv[1]; argv[0] = &fdata[0]; Con::setData(itr->type, (void *)(S32(obj) + itr->offset), 0, 1, argv, itr->table, itr->flag); } // get the dynamic field data SimFieldDictionary * fieldDictionary = mTarget->getFieldDictionary(); for(SimFieldDictionaryIterator ditr(fieldDictionary); *ditr; ++ditr) { SimFieldDictionary::Entry * entry = (*ditr); char buf[1024]; dSprintf(buf, sizeof(buf), "InspectDynamic%s", entry->slotName); GuiControl * editCtrl = static_cast(Sim::findObject(buf)); if(!editCtrl) continue; const char * newValue = editCtrl->getScriptValue(); dStrcpy(buf, newValue ? newValue : ""); collapseEscape(buf); fieldDictionary->setFieldValue(entry->slotName, buf); } mTarget->inspectPostApply(); //now re-inspect the object inspect(mTarget); } //------------------------------------------------------------------------------ static void cInspect(SimObject *obj, S32, const char **argv) { GuiInspector * inspector = static_cast(obj); SimObject * target = Sim::findObject(argv[2]); if(!target) { Con::printf("%s(): invalid object: %s", argv[0], argv[2]); return; } inspector->inspect(target); } static void cApply(SimObject *obj, S32, const char **argv) { GuiInspector *inspector = static_cast(obj); inspector->apply(argv[2]); } void GuiInspector::consoleInit() { Con::addCommand("GuiInspector", "inspect", cInspect, "inspector.inspect(obj)", 3, 3); Con::addCommand("GuiInspector", "apply", cApply, "inspector.apply(newName)", 3, 3); } void GuiInspector::initPersistFields() { Parent::initPersistFields(); addField("editControlOffset", TypeS32, Offset(mEditControlOffset, GuiInspector)); addField("entryHeight", TypeS32, Offset(mEntryHeight, GuiInspector)); addField("textExtent", TypeS32, Offset(mTextExtent, GuiInspector)); addField("entrySpacing", TypeS32, Offset(mEntrySpacing, GuiInspector)); addField("maxMenuExtent", TypeS32, Offset(mMaxMenuExtent, GuiInspector)); }