From c150afebaac2a72558dcf9997138151baf8561bf Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Mon, 20 Sep 2021 13:50:47 -0500 Subject: [PATCH 01/10] particle cleanups misc dupe code cleanup bits. safeties for the varous flavors of void ParticleEmitter::setup ideally we circle back to break some of that logic on out to shared steps --- Engine/source/T3D/fx/particleEmitter.cpp | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/Engine/source/T3D/fx/particleEmitter.cpp b/Engine/source/T3D/fx/particleEmitter.cpp index 5f7b0829d..45acd1ecb 100644 --- a/Engine/source/T3D/fx/particleEmitter.cpp +++ b/Engine/source/T3D/fx/particleEmitter.cpp @@ -1551,13 +1551,10 @@ void ParticleEmitter::updateBBox() for (Particle* part = part_list_head.next; part != NULL; part = part->next) { - for (Particle* part = part_list_head.next; part != NULL; part = part->next) - { - Point3F particleSize(part->size * 0.5f); - F32 motion = getMax((part->vel.len() * part->totalLifetime / 1000.0f), 1.0f); - minPt.setMin(part->pos - particleSize - Point3F(motion)); - maxPt.setMax(part->pos + particleSize + Point3F(motion)); - } + Point3F particleSize(part->size * 0.5f); + F32 motion = getMax((part->vel.len() * part->totalLifetime / 1000.0f), 1.0f); + minPt.setMin(part->pos - particleSize - Point3F(motion)); + maxPt.setMax(part->pos + particleSize + Point3F(motion)); } mObjBox = Box3F(minPt, maxPt); @@ -1675,8 +1672,8 @@ void ParticleEmitter::addParticle(const Point3F& pos, const Point3F& axis, const } else { - U32 dBlockIndex = gRandGen.randI() % mDataBlock->particleDataBlocks.size(); - mDataBlock->particleDataBlocks[dBlockIndex]->initializeParticle(pNew, vel); + dBlockIndex = gRandGen.randI() % mDataBlock->particleDataBlocks.size(); + mDataBlock->particleDataBlocks[dBlockIndex]->initializeParticle(pNew, vel); } updateKeyData( pNew ); @@ -2220,7 +2217,7 @@ void ParticleEmitter::setupOriented( Particle *part, LinearColorF partCol = mLerp( part->color, ( part->color * ambientColor ), ambientLerp ); const ColorI color = partCol.toColorI(); // Here we deal with UVs for animated particle (oriented) - if (part->dataBlock->animateTexture) + if (part->dataBlock->animateTexture && !part->dataBlock->animTexFrames.empty()) { // Let particle compute the UV indices for current frame S32 fm = (S32)(part->currentAge*(1.0f/1000.0f)*part->dataBlock->framesPerSec); @@ -2331,7 +2328,7 @@ void ParticleEmitter::setupAligned( const Particle *part, LinearColorF partCol = mLerp( part->color, ( part->color * ambientColor ), ambientLerp ); const ColorI color = partCol.toColorI(); // Here we deal with UVs for animated particle - if (part->dataBlock->animateTexture) + if (part->dataBlock->animateTexture && !part->dataBlock->animTexFrames.empty()) { // Let particle compute the UV indices for current frame S32 fm = (S32)(part->currentAge*(1.0f/1000.0f)*part->dataBlock->framesPerSec); @@ -2520,7 +2517,7 @@ void ParticleEmitter::setupRibbon(Particle *part, ColorI pCol = partCol.toColorI(); // Here we deal with UVs for animated particle (oriented) - if (part->dataBlock->animateTexture) + if (part->dataBlock->animateTexture && !part->dataBlock->animTexFrames.empty()) { // Let particle compute the UV indices for current frame S32 fm = (S32)(part->currentAge*(1.0f / 1000.0f)*part->dataBlock->framesPerSec); From 2d50f52cf135fd8e6ebed638767dd60fd34d01c0 Mon Sep 17 00:00:00 2001 From: Jeff Hutchinson Date: Mon, 20 Sep 2021 21:00:33 -0400 Subject: [PATCH 02/10] Allow local variables to be used in eval. --- Engine/source/console/astNodes.cpp | 55 ++------------------------ Engine/source/console/codeBlock.cpp | 15 ++++++- Engine/source/console/compiledEval.cpp | 3 +- Engine/source/console/compiler.cpp | 42 ++++++++++++++++++++ Engine/source/console/compiler.h | 29 ++++++++++++++ 5 files changed, 90 insertions(+), 54 deletions(-) diff --git a/Engine/source/console/astNodes.cpp b/Engine/source/console/astNodes.cpp index 091f6cf1c..aaca1ea06 100644 --- a/Engine/source/console/astNodes.cpp +++ b/Engine/source/console/astNodes.cpp @@ -56,57 +56,7 @@ namespace Compiler using namespace Compiler; -class FuncVars -{ - struct Var - { - S32 reg; - TypeReq currentType; - StringTableEntry name; - bool isConstant; - }; - -public: - S32 assign(StringTableEntry var, TypeReq currentType, S32 lineNumber, bool isConstant = false) - { - std::unordered_map::iterator found = vars.find(var); - if (found != vars.end()) - { - AssertISV(!found->second.isConstant, avar("Reassigning variable %s when it is a constant. File: %s Line : %d", var, CodeBlock::smCurrentParser->getCurrentFile(), lineNumber)); - return found->second.reg; - } - - S32 id = counter++; - vars[var] = { id, currentType, var, isConstant }; - variableNameMap[id] = var; - - return id; - } - - S32 lookup(StringTableEntry var, S32 lineNumber) - { - std::unordered_map::iterator found = vars.find(var); - AssertISV(found != vars.end(), avar("Variable %s referenced before used when compiling script. File: %s Line: %d", var, CodeBlock::smCurrentParser->getCurrentFile(), lineNumber)); - return found->second.reg; - } - - TypeReq lookupType(StringTableEntry var, S32 lineNumber) - { - std::unordered_map::iterator found = vars.find(var); - - AssertISV(found != vars.end(), avar("Variable %s referenced before used when compiling script. File: %s Line: %d", var, CodeBlock::smCurrentParser->getCurrentFile(), lineNumber)); - return found->second.currentType; - } - - inline S32 count() { return counter; } - - std::unordered_map variableNameMap; - -private: - std::unordered_map vars; - S32 counter = 0; -}; - +FuncVars gEvalFuncVars; FuncVars* gFuncVars = NULL; inline FuncVars* getFuncVars(S32 lineNumber) @@ -1602,7 +1552,8 @@ U32 FunctionDeclStmtNode::compileStmt(CodeStream& codeStream, U32 ip) tbl->add(fnName, nameSpace, varName); } - gFuncVars = NULL; + // In eval mode, global func vars are allowed. + gFuncVars = gIsEvalCompile ? &gEvalFuncVars : NULL; return ip; } diff --git a/Engine/source/console/codeBlock.cpp b/Engine/source/console/codeBlock.cpp index 1d1ad7593..f03ec25f6 100644 --- a/Engine/source/console/codeBlock.cpp +++ b/Engine/source/console/codeBlock.cpp @@ -37,6 +37,9 @@ CodeBlock * CodeBlock::smCodeBlockList = NULL; CodeBlock * CodeBlock::smCurrentCodeBlock = NULL; ConsoleParser *CodeBlock::smCurrentParser = NULL; +extern FuncVars gEvalFuncVars; +extern FuncVars* gFuncVars; + //------------------------------------------------------------------------- CodeBlock::CodeBlock() @@ -491,6 +494,8 @@ bool CodeBlock::compile(const char *codeFileName, StringTableEntry fileName, con chompUTF8BOM(inScript, &script); gSyntaxError = false; + gIsEvalCompile = false; + gFuncVars = NULL; consoleAllocReset(); @@ -629,6 +634,11 @@ ConsoleValue CodeBlock::compileExec(StringTableEntry fileName, const char *inStr addToCodeList(); gStatementList = NULL; + + // we are an eval compile if we don't have a file name associated (no exec) + gIsEvalCompile = fileName == NULL; + // In eval mode, global func vars are allowed. + gFuncVars = gIsEvalCompile ? &gEvalFuncVars : NULL; // Set up the parser. smCurrentParser = getParserForFile(fileName); @@ -667,6 +677,8 @@ ConsoleValue CodeBlock::compileExec(StringTableEntry fileName, const char *inStr codeStream.emit(OP_RETURN_VOID); codeStream.emitCodeStream(&codeSize, &code, &lineBreakPairs); + + S32 localRegisterCount = gIsEvalCompile ? gEvalFuncVars.count() : 0; consoleAllocReset(); @@ -683,7 +695,8 @@ ConsoleValue CodeBlock::compileExec(StringTableEntry fileName, const char *inStr if (lastIp + 1 != codeSize) Con::warnf(ConsoleLogEntry::General, "precompile size mismatch, precompile: %d compile: %d", codeSize, lastIp); - return std::move(exec(0, fileName, NULL, 0, 0, noCalls, NULL, setFrame)); + // repurpose argc as local register counter for global state + return std::move(exec(0, fileName, NULL, localRegisterCount, 0, noCalls, NULL, setFrame)); } //------------------------------------------------------------------------- diff --git a/Engine/source/console/compiledEval.cpp b/Engine/source/console/compiledEval.cpp index 561e1f04a..360c13c90 100644 --- a/Engine/source/console/compiledEval.cpp +++ b/Engine/source/console/compiledEval.cpp @@ -693,7 +693,8 @@ ConsoleValue CodeBlock::exec(U32 ip, const char* functionName, Namespace* thisNa // Do we want this code to execute using a new stack frame? if (setFrame < 0) { - gEvalState.pushFrame(NULL, NULL, 0); + // argc is the local count for eval + gEvalState.pushFrame(NULL, NULL, argc); gCallStack.pushFrame(0); popFrame = true; } diff --git a/Engine/source/console/compiler.cpp b/Engine/source/console/compiler.cpp index 1ab6e7b22..13839feda 100644 --- a/Engine/source/console/compiler.cpp +++ b/Engine/source/console/compiler.cpp @@ -34,6 +34,8 @@ #include "console/simBase.h" +extern FuncVars gEvalFuncVars; + namespace Compiler { @@ -86,6 +88,7 @@ namespace Compiler //------------------------------------------------------------ bool gSyntaxError = false; + bool gIsEvalCompile = false; //------------------------------------------------------------ @@ -121,6 +124,7 @@ namespace Compiler getFunctionStringTable().reset(); getIdentTable().reset(); getFunctionVariableMappingTable().reset(); + gEvalFuncVars.clear(); } void *consoleAlloc(U32 size) { return gConsoleAllocator.alloc(size); } @@ -132,6 +136,44 @@ namespace Compiler using namespace Compiler; +S32 FuncVars::assign(StringTableEntry var, TypeReq currentType, S32 lineNumber, bool isConstant) +{ + std::unordered_map::iterator found = vars.find(var); + if (found != vars.end()) + { + AssertISV(!found->second.isConstant, avar("Reassigning variable %s when it is a constant. File: %s Line : %d", var, CodeBlock::smCurrentParser->getCurrentFile(), lineNumber)); + return found->second.reg; + } + + S32 id = counter++; + vars[var] = { id, currentType, var, isConstant }; + variableNameMap[id] = var; + + return id; +} + +S32 FuncVars::lookup(StringTableEntry var, S32 lineNumber) +{ + std::unordered_map::iterator found = vars.find(var); + AssertISV(found != vars.end(), avar("Variable %s referenced before used when compiling script. File: %s Line: %d", var, CodeBlock::smCurrentParser->getCurrentFile(), lineNumber)); + return found->second.reg; +} + +TypeReq FuncVars::lookupType(StringTableEntry var, S32 lineNumber) +{ + std::unordered_map::iterator found = vars.find(var); + + AssertISV(found != vars.end(), avar("Variable %s referenced before used when compiling script. File: %s Line: %d", var, CodeBlock::smCurrentParser->getCurrentFile(), lineNumber)); + return found->second.currentType; +} + +void FuncVars::clear() +{ + vars.clear(); + variableNameMap.clear(); + counter = 0; +} + //------------------------------------------------------------------------- diff --git a/Engine/source/console/compiler.h b/Engine/source/console/compiler.h index 117576a61..24067257f 100644 --- a/Engine/source/console/compiler.h +++ b/Engine/source/console/compiler.h @@ -276,6 +276,35 @@ namespace Compiler void consoleAllocReset(); extern bool gSyntaxError; + extern bool gIsEvalCompile; +}; + +class FuncVars +{ + struct Var + { + S32 reg; + TypeReq currentType; + StringTableEntry name; + bool isConstant; + }; + +public: + S32 assign(StringTableEntry var, TypeReq currentType, S32 lineNumber, bool isConstant = false); + + S32 lookup(StringTableEntry var, S32 lineNumber); + + TypeReq lookupType(StringTableEntry var, S32 lineNumber); + + inline S32 count() { return counter; } + + std::unordered_map variableNameMap; + + void clear(); + +private: + std::unordered_map vars; + S32 counter = 0; }; /// Utility class to emit and patch bytecode From 901228c3a8f2208227e077472fdc73053672e434 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Tue, 21 Sep 2021 23:19:00 -0500 Subject: [PATCH 03/10] kill splashscreen on nonwindows was worth a shot, but it's playing up way too much to focus on at present --- Templates/BaseGame/game/main.tscript.in | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Templates/BaseGame/game/main.tscript.in b/Templates/BaseGame/game/main.tscript.in index 4e83ff2b2..800e9625a 100644 --- a/Templates/BaseGame/game/main.tscript.in +++ b/Templates/BaseGame/game/main.tscript.in @@ -19,9 +19,14 @@ ModuleDatabase.LoadExplicit( "CoreModule" ); // Display a splash window immediately to improve app responsiveness before // engine is initialized and main window created. if ($Server::Dedicated == false) - displaySplashWindow($Core::splashWindowImage); +{ + if ($platform $= "windows") + displaySplashWindow($Core::splashWindowImage); +} else +{ $Video::forceDisplayAdapter = -1; +} //----------------------------------------------------------------------------- // Load any gameplay modules @@ -45,7 +50,7 @@ else } } -if ($Server::Dedicated == false) +if ( ($Server::Dedicated == false) && ($platform $= "windows") ) closeSplashWindow(); echo("Engine initialized..."); \ No newline at end of file From 4cfb3c4f04bb5ab0c239bc733508295ae1a6c16a Mon Sep 17 00:00:00 2001 From: Robert MacGregor Date: Wed, 22 Sep 2021 20:28:39 -0400 Subject: [PATCH 04/10] * [Linux] BugFix: Corrections to allow the Linux win console (Ie. the terminal you ran the game from) to act as a console input. --- Engine/source/platformX86UNIX/x86UNIXConsole.cpp | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/Engine/source/platformX86UNIX/x86UNIXConsole.cpp b/Engine/source/platformX86UNIX/x86UNIXConsole.cpp index 88e17f38e..85828b202 100644 --- a/Engine/source/platformX86UNIX/x86UNIXConsole.cpp +++ b/Engine/source/platformX86UNIX/x86UNIXConsole.cpp @@ -41,7 +41,10 @@ StdConsole *stdConsole = NULL; DefineEngineFunction(enableWinConsole, void, (bool _enable),, "enableWinConsole(bool);") { if (stdConsole) - stdConsole->enable(_enable); + { + stdConsole->enable(_enable); + stdConsole->enableInput(_enable); + } } void StdConsole::create() @@ -141,6 +144,9 @@ StdConsole::StdConsole() stdIn = dup(0); stdErr = dup(2); + // Ensure in buffer is null terminated after allocation + inbuf[0] = 0x00; + iCmdIndex = 0; stdConsoleEnabled = false; stdConsoleInputEnabled = false; @@ -195,11 +201,14 @@ void StdConsole::processConsoleLine(const char *consoleLine) { if(stdConsoleEnabled) { - inbuf[inpos] = 0; if(lineOutput) printf("%s\n", consoleLine); else - printf("%c%s\n%s%s", '\r', consoleLine, Con::getVariable("Con::Prompt"), inbuf); + { + // Clear current line before outputting the console line. This prevents prompt text from overflowing onto normal output. + printf("%c[2K", 27); + printf("%c%s\n%s%s", '\r', consoleLine, Con::getVariable("Con::Prompt"), inbuf); + } } } @@ -323,6 +332,7 @@ void StdConsole::process() printf("%s", Con::getVariable("Con::Prompt")); inpos = outpos = 0; + inbuf[0] = 0x00; // Ensure inbuf is NULL terminated after sending out command break; case 27: // JMQTODO: are these magic numbers keyboard map specific? From 55063601e6aacc6aca62855a2c575450666a57ea Mon Sep 17 00:00:00 2001 From: Robert MacGregor Date: Thu, 23 Sep 2021 22:58:25 -0400 Subject: [PATCH 05/10] * [Projectile] BugFix: Correct the onAdd callback not being raised for projectiles. --- Engine/source/T3D/projectile.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Engine/source/T3D/projectile.cpp b/Engine/source/T3D/projectile.cpp index 739daf4a5..b21fe9542 100644 --- a/Engine/source/T3D/projectile.cpp +++ b/Engine/source/T3D/projectile.cpp @@ -786,6 +786,8 @@ bool Projectile::onAdd() // If we're on the server, we need to inherit some of our parent's velocity // mCurrTick = 0; + + scriptOnAdd(); } else { From 12f4af51f557cc76a4d018fe262b816eae58b119 Mon Sep 17 00:00:00 2001 From: Robert MacGregor Date: Fri, 24 Sep 2021 17:58:17 -0400 Subject: [PATCH 06/10] * [Editor] BugFix: Fix a crash that sometimes occurs when groups of objects are deleted in the world editor. --- Engine/source/gui/worldEditor/worldEditor.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Engine/source/gui/worldEditor/worldEditor.cpp b/Engine/source/gui/worldEditor/worldEditor.cpp index d6642cd1c..dfccf8533 100644 --- a/Engine/source/gui/worldEditor/worldEditor.cpp +++ b/Engine/source/gui/worldEditor/worldEditor.cpp @@ -1993,12 +1993,12 @@ void WorldEditor::on3DMouseMove(const Gui3DMouseEvent & event) if ( !mHitObject ) { SceneObject *hitObj = NULL; - if ( collide(event, &hitObj) && hitObj->isSelectionEnabled() && !objClassIgnored(hitObj) ) + if ( collide(event, &hitObj) && !hitObj->isDeleted() && hitObj->isSelectionEnabled() && !objClassIgnored(hitObj) ) { mHitObject = hitObj; } } - + mLastMouseEvent = event; } From d7919950bfbe217650fbb457f13fe31bcbf6e4a0 Mon Sep 17 00:00:00 2001 From: Robert MacGregor Date: Fri, 24 Sep 2021 18:34:57 -0400 Subject: [PATCH 07/10] * [Editor] BugFix: Correct an object spawning error when attempting to drag and drop datablocks from the asset browser where they would always spawn at origin. --- .../game/tools/worldEditor/scripts/editors/creator.ed.tscript | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Templates/BaseGame/game/tools/worldEditor/scripts/editors/creator.ed.tscript b/Templates/BaseGame/game/tools/worldEditor/scripts/editors/creator.ed.tscript index 08bd46598..8fce4a3fd 100644 --- a/Templates/BaseGame/game/tools/worldEditor/scripts/editors/creator.ed.tscript +++ b/Templates/BaseGame/game/tools/worldEditor/scripts/editors/creator.ed.tscript @@ -227,7 +227,7 @@ function EWCreatorWindow::createObject( %this, %cmd ) %this.setNewObjectGroup( getScene(0) ); pushInstantGroup(); - %objId = eval(%cmd); + %objId = eval("return " @ %cmd); popInstantGroup(); if( isObject( %objId ) ) From f1ee4f781b2ead4f881665784cd4751d7b87cb69 Mon Sep 17 00:00:00 2001 From: Jeff Hutchinson Date: Fri, 24 Sep 2021 19:09:43 -0400 Subject: [PATCH 08/10] Material editor fixes from eval cleanup. --- .../scripts/materialEditor.ed.tscript | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/Templates/BaseGame/game/tools/materialEditor/scripts/materialEditor.ed.tscript b/Templates/BaseGame/game/tools/materialEditor/scripts/materialEditor.ed.tscript index bdca293da..8a21c9907 100644 --- a/Templates/BaseGame/game/tools/materialEditor/scripts/materialEditor.ed.tscript +++ b/Templates/BaseGame/game/tools/materialEditor/scripts/materialEditor.ed.tscript @@ -1097,9 +1097,6 @@ function MaterialEditorGui::updateActiveMaterial(%this, %propertyField, %value, { MaterialEditorGui.setMaterialDirty(); - if(%value $= "") - %value = "\"\""; - // Here is where we handle undo actions with slider controls. We want to be able to // undo every onMouseUp; so we overrite the same undo action when necessary in order // to achieve this desired effect. @@ -1130,7 +1127,7 @@ function MaterialEditorGui::updateActiveMaterial(%this, %propertyField, %value, if (MaterialEditorGui.livePreview == true) { - MaterialEditorGui.setFieldValue(%propertyField, %value); + MaterialEditorGui.currentMaterial.setFieldValue(%propertyField, %value); MaterialEditorGui.currentMaterial.flush(); MaterialEditorGui.currentMaterial.reload(); } @@ -1263,8 +1260,8 @@ function MaterialEditorGui::doUpdateTextureMap( %this, %assetId ) %bitmap = strreplace(%bitmap,"tools/materialEditor/scripts/",""); %bitmapCtrl.setBitmap(%bitmap); %textCtrl.setText(%assetId); - MaterialEditorGui.updateActiveMaterial(%type @ "Map[" @ %layer @ "]","\"\""); - MaterialEditorGui.updateActiveMaterial(%type @ "MapAsset[" @ %layer @ "]","\"" @ %assetId @ "\""); + MaterialEditorGui.updateActiveMaterial(%type @ "Map[" @ %layer @ "]",""); + MaterialEditorGui.updateActiveMaterial(%type @ "MapAsset[" @ %layer @ "]", %assetId); } } @@ -1276,7 +1273,7 @@ function MaterialEditorGui::updateDetailScale(%this,%newScale) { %layer = MaterialEditorGui.currentLayer; - %detailScale = "\"" @ %newScale SPC %newScale @ "\""; + %detailScale = %newScale SPC %newScale; MaterialEditorGui.updateActiveMaterial("detailScale[" @ %layer @ "]", %detailScale); } @@ -1284,7 +1281,7 @@ function MaterialEditorGui::updateDetailNormalStrength(%this,%newStrength) { %layer = MaterialEditorGui.currentLayer; - %detailStrength = "\"" @ %newStrength @ "\""; + %detailStrength = %newStrength; MaterialEditorGui.updateActiveMaterial("detailNormalMapStrength[" @ %layer @ "]", %detailStrength); } @@ -1295,7 +1292,7 @@ function MaterialEditorGui::updateRotationOffset(%this, %isSlider, %onMouseUp) %Y = MaterialEditorPropertiesWindow-->RotationTextEditV.getText(); MaterialEditorPropertiesWindow-->RotationCrosshair.setPosition(45*mAbs(%X)-2, 45*mAbs(%Y)-2); - MaterialEditorGui.updateActiveMaterial("rotPivotOffset[" @ %layer @ "]","\"" @ %X SPC %Y @ "\"",%isSlider,%onMouseUp); + MaterialEditorGui.updateActiveMaterial("rotPivotOffset[" @ %layer @ "]", %X SPC %Y,%isSlider,%onMouseUp); } function MaterialEditorGui::updateRotationSpeed(%this, %isSlider, %onMouseUp) @@ -1457,7 +1454,7 @@ function MaterialEditorGui::syncGuiColor(%this, %guiCtrl, %propname, %color) %a = getWord(%color,3); %colorSwatch = (%r SPC %g SPC %b SPC %a); - %color = "\"" @ %r SPC %g SPC %b SPC %a @ "\""; + %color = %r SPC %g SPC %b SPC %a; %guiCtrl.color = %colorSwatch; MaterialEditorGui.updateActiveMaterial(%propName, %color); From 755bbacaa0ee10a5b91c27ab29e604947c7a4adb Mon Sep 17 00:00:00 2001 From: Jeff Hutchinson Date: Fri, 24 Sep 2021 19:32:57 -0400 Subject: [PATCH 09/10] Fix specific usage of Con::executef where it was not being assigned to a ConsoleValue before getting it's data out of it. --- Engine/source/Verve/Core/Persistence/VPersistence.h | 5 +++-- Engine/source/afx/afxEffectron.cpp | 3 ++- Engine/source/afx/afxSelectron.cpp | 6 ++++-- Engine/source/console/arrayObject.cpp | 6 ++++-- Engine/source/console/simObjectList.cpp | 3 ++- Engine/source/environment/editors/guiRiverEditorCtrl.cpp | 3 ++- Engine/source/forest/editor/forestBrushTool.cpp | 5 +++-- Engine/source/gui/controls/guiTreeViewCtrl.cpp | 4 +++- Engine/source/gui/editor/guiPopupMenuCtrl.cpp | 4 +++- Engine/source/gui/editor/popupMenu.cpp | 3 ++- Engine/source/gui/worldEditor/worldEditor.cpp | 4 +++- Engine/source/module/moduleManager.cpp | 6 ++++-- Engine/source/util/messaging/scriptMsgListener.cpp | 4 ---- 13 files changed, 35 insertions(+), 21 deletions(-) diff --git a/Engine/source/Verve/Core/Persistence/VPersistence.h b/Engine/source/Verve/Core/Persistence/VPersistence.h index edbc892c8..a350005fc 100644 --- a/Engine/source/Verve/Core/Persistence/VPersistence.h +++ b/Engine/source/Verve/Core/Persistence/VPersistence.h @@ -250,8 +250,9 @@ namespace VPersistence if ( object->isMethod( "onAdd" ) ) { // Callback. - const char *retValue = Con::executef( object, "onAdd" ); - if ( !dAtob( retValue ) ) + ConsoleValue cValue = Con::executef( object, "onAdd" ); + + if ( !cValue.getBool() ) { // Delete. object->deleteObject(); diff --git a/Engine/source/afx/afxEffectron.cpp b/Engine/source/afx/afxEffectron.cpp index 1c68256c1..f90d20f73 100644 --- a/Engine/source/afx/afxEffectron.cpp +++ b/Engine/source/afx/afxEffectron.cpp @@ -989,9 +989,10 @@ afxEffectron::start_effect(afxEffectronData* datablock, SimObject* extra) } // CALL SCRIPT afxEffectronData::onPreactivate(%params, %extra) - const char* result = Con::executef(datablock, "onPreactivate", + ConsoleValue cValue = Con::executef(datablock, "onPreactivate", Con::getIntArg(param_holder->getId()), (extra) ? Con::getIntArg(extra->getId()) : ""); + const char* result = cValue.getString(); if (result && result[0] != '\0' && !dAtob(result)) { #if defined(TORQUE_DEBUG) diff --git a/Engine/source/afx/afxSelectron.cpp b/Engine/source/afx/afxSelectron.cpp index 63157a4f1..1b6194b9a 100644 --- a/Engine/source/afx/afxSelectron.cpp +++ b/Engine/source/afx/afxSelectron.cpp @@ -1068,9 +1068,11 @@ afxSelectron::start_selectron(SceneObject* picked, U8 subcode, SimObject* extra) } // CALL SCRIPT afxSelectronData::onPreactivate(%params, %extra) - const char* result = Con::executef(datablock, "onPreactivate", + ConsoleValue cValue = Con::executef(datablock, "onPreactivate", Con::getIntArg(param_holder->getId()), - (extra) ? Con::getIntArg(extra->getId()) : "").getString(); + (extra) ? Con::getIntArg(extra->getId()) : ""); + + const char* result = cValue.getString(); if (result && result[0] != '\0' && !dAtob(result)) { #if defined(TORQUE_DEBUG) diff --git a/Engine/source/console/arrayObject.cpp b/Engine/source/console/arrayObject.cpp index 92458852d..68b34059e 100644 --- a/Engine/source/console/arrayObject.cpp +++ b/Engine/source/console/arrayObject.cpp @@ -103,7 +103,8 @@ S32 QSORT_CALLBACK ArrayObject::_keyFunctionCompare( const void* a, const void* ArrayObject::Element* ea = ( ArrayObject::Element* )( a ); ArrayObject::Element* eb = ( ArrayObject::Element* )( b ); - S32 result = dAtoi(Con::executef((const char*)smCompareFunction, ea->key, eb->key)); + ConsoleValue cValue = Con::executef((const char*)smCompareFunction, ea->key, eb->key); + S32 result = cValue.getInt(); S32 res = result < 0 ? -1 : ( result > 0 ? 1 : 0 ); return ( smDecreasing ? -res : res ); } @@ -113,7 +114,8 @@ S32 QSORT_CALLBACK ArrayObject::_valueFunctionCompare( const void* a, const void ArrayObject::Element* ea = ( ArrayObject::Element* )( a ); ArrayObject::Element* eb = ( ArrayObject::Element* )( b ); - S32 result = dAtoi( Con::executef( (const char*)smCompareFunction, ea->value, eb->value ) ); + ConsoleValue cValue = Con::executef( (const char*)smCompareFunction, ea->value, eb->value ); + S32 result = cValue.getInt(); S32 res = result < 0 ? -1 : ( result > 0 ? 1 : 0 ); return ( smDecreasing ? -res : res ); } diff --git a/Engine/source/console/simObjectList.cpp b/Engine/source/console/simObjectList.cpp index 8388f6e9d..ac286f22b 100644 --- a/Engine/source/console/simObjectList.cpp +++ b/Engine/source/console/simObjectList.cpp @@ -118,7 +118,8 @@ S32 QSORT_CALLBACK SimObjectList::_callbackSort( const void *a, const void *b ) static char idB[64]; dSprintf( idB, sizeof( idB ), "%d", objB->getId() ); - return dAtoi( Con::executef( (const char*)smSortScriptCallbackFn, idA, idB ) ); + ConsoleValue cValue = Con::executef( (const char*)smSortScriptCallbackFn, idA, idB ); + return cValue.getInt(); } void SimObjectList::scriptSort( const String &scriptCallback ) diff --git a/Engine/source/environment/editors/guiRiverEditorCtrl.cpp b/Engine/source/environment/editors/guiRiverEditorCtrl.cpp index ae9084588..e650fdf59 100644 --- a/Engine/source/environment/editors/guiRiverEditorCtrl.cpp +++ b/Engine/source/environment/editors/guiRiverEditorCtrl.cpp @@ -438,7 +438,8 @@ void GuiRiverEditorCtrl::_process3DMouseDown( const Gui3DMouseEvent& event ) return; } - const char *res = Con::executef( this, "createRiver" ); + ConsoleValue cValue = Con::executef( this, "createRiver" ); + const char* res = cValue.getString(); River *newRiver; if ( !Sim::findObject( res, newRiver ) ) diff --git a/Engine/source/forest/editor/forestBrushTool.cpp b/Engine/source/forest/editor/forestBrushTool.cpp index d0d64d517..5b9278ea6 100644 --- a/Engine/source/forest/editor/forestBrushTool.cpp +++ b/Engine/source/forest/editor/forestBrushTool.cpp @@ -588,7 +588,8 @@ void ForestBrushTool::_collectElements() if ( !Sim::findObject( "ForestEditBrushTree", brushTree ) ) return; - const char* objectIdList = Con::executef( brushTree, "getSelectedObjectList" ); + ConsoleValue cValue = Con::executef( brushTree, "getSelectedObjectList" ); + const char* objectIdList = cValue.getString(); // Collect those objects in a vector and mark them as selected. @@ -685,4 +686,4 @@ bool ForestBrushTool::getGroundAt( const Point3F &worldPt, F32 *zValueOut, Vecto DefineEngineMethod( ForestBrushTool, collectElements, void, (), , "" ) { object->collectElements(); -} \ No newline at end of file +} diff --git a/Engine/source/gui/controls/guiTreeViewCtrl.cpp b/Engine/source/gui/controls/guiTreeViewCtrl.cpp index e2804b1f1..aaed67a78 100644 --- a/Engine/source/gui/controls/guiTreeViewCtrl.cpp +++ b/Engine/source/gui/controls/guiTreeViewCtrl.cpp @@ -637,7 +637,9 @@ void GuiTreeViewCtrl::Item::getTooltipText(U32 bufLen, char *buf) method += pClassName; if(mParentControl->isMethod(method.c_str())) { - const char* tooltip = Con::executef( mParentControl, method.c_str(), pObject->getIdString() ); + ConsoleValue cValue = Con::executef( mParentControl, method.c_str(), pObject->getIdString() ); + const char* tooltip = cValue.getString(); + dsize_t len = dStrlen(buf); S32 newBufLen = bufLen-len; if(dStrlen(tooltip) > 0 && newBufLen > 0) diff --git a/Engine/source/gui/editor/guiPopupMenuCtrl.cpp b/Engine/source/gui/editor/guiPopupMenuCtrl.cpp index 7c63ebe52..ba04467c2 100644 --- a/Engine/source/gui/editor/guiPopupMenuCtrl.cpp +++ b/Engine/source/gui/editor/guiPopupMenuCtrl.cpp @@ -226,7 +226,9 @@ void GuiPopupMenuTextListCtrl::onMouseUp(const GuiEvent &event) if (item) { if (item->mEnabled) - dAtob(Con::executef(mPopup, "onSelectItem", Con::getIntArg(getSelectedCell().y), item->mText.isNotEmpty() ? item->mText : "")); + { + Con::executef(mPopup, "onSelectItem", Con::getIntArg(getSelectedCell().y), item->mText.isNotEmpty() ? item->mText : ""); + } } } diff --git a/Engine/source/gui/editor/popupMenu.cpp b/Engine/source/gui/editor/popupMenu.cpp index 14fff6844..6e7cb39ee 100644 --- a/Engine/source/gui/editor/popupMenu.cpp +++ b/Engine/source/gui/editor/popupMenu.cpp @@ -288,7 +288,8 @@ bool PopupMenu::canHandleID(U32 id) bool PopupMenu::handleSelect(U32 command, const char *text /* = NULL */) { - return dAtob(Con::executef(this, "onSelectItem", Con::getIntArg(command), text ? text : "")); + ConsoleValue cValue = Con::executef(this, "onSelectItem", Con::getIntArg(command), text ? text : ""); + return cValue.getBool(); } ////////////////////////////////////////////////////////////////////////// diff --git a/Engine/source/gui/worldEditor/worldEditor.cpp b/Engine/source/gui/worldEditor/worldEditor.cpp index d6642cd1c..d2aa75c4d 100644 --- a/Engine/source/gui/worldEditor/worldEditor.cpp +++ b/Engine/source/gui/worldEditor/worldEditor.cpp @@ -461,7 +461,9 @@ bool WorldEditor::pasteSelection( bool dropSel ) SimGroup *targetGroup = NULL; if( isMethod( "getNewObjectGroup" ) ) { - const char* targetGroupName = Con::executef( this, "getNewObjectGroup" ); + ConsoleValue cValue = Con::executef( this, "getNewObjectGroup" ); + const char* targetGroupName = cValue.getString(); + if( targetGroupName && targetGroupName[ 0 ] && !Sim::findObject( targetGroupName, targetGroup) ) Con::errorf( "WorldEditor::pasteSelection() - no SimGroup called '%s'", targetGroupName ); } diff --git a/Engine/source/module/moduleManager.cpp b/Engine/source/module/moduleManager.cpp index f91ae060d..08db0da6c 100644 --- a/Engine/source/module/moduleManager.cpp +++ b/Engine/source/module/moduleManager.cpp @@ -397,7 +397,8 @@ bool ModuleManager::loadModuleGroup( const char* pModuleGroup ) if ( pLoadReadyModuleDefinition->getModuleScriptFilePath() != StringTable->EmptyString() ) { // Yes, so execute the script file. - const bool scriptFileExecuted = dAtob( Con::executef("exec", pLoadReadyModuleDefinition->getModuleScriptFilePath() ) ); + ConsoleValue cValue = Con::executef("exec", pLoadReadyModuleDefinition->getModuleScriptFilePath()); + const bool scriptFileExecuted = cValue.getBool(); // Did we execute the script file? if ( scriptFileExecuted ) @@ -784,7 +785,8 @@ bool ModuleManager::loadModuleExplicit( const char* pModuleId, const U32 version if ( pLoadReadyModuleDefinition->getModuleScriptFilePath() != StringTable->EmptyString() ) { // Yes, so execute the script file. - const bool scriptFileExecuted = dAtob( Con::executef("exec", pLoadReadyModuleDefinition->getModuleScriptFilePath() ) ); + ConsoleValue cValue = Con::executef("exec", pLoadReadyModuleDefinition->getModuleScriptFilePath()); + const bool scriptFileExecuted = cValue.getBool(); // Did we execute the script file? if ( !scriptFileExecuted ) diff --git a/Engine/source/util/messaging/scriptMsgListener.cpp b/Engine/source/util/messaging/scriptMsgListener.cpp index fa47b7e7c..f64d2a6b2 100644 --- a/Engine/source/util/messaging/scriptMsgListener.cpp +++ b/Engine/source/util/messaging/scriptMsgListener.cpp @@ -121,7 +121,6 @@ IMPLEMENT_CALLBACK( ScriptMsgListener, onMessageReceived, bool, ( const char* qu bool ScriptMsgListener::onMessageReceived(StringTableEntry queue, const char* event, const char* data) { return onMessageReceived_callback(queue, event, data); - //return dAtob(Con::executef(this, "onMessageReceived", queue, event, data)); } IMPLEMENT_CALLBACK( ScriptMsgListener, onMessageObjectReceived, bool, ( const char* queue, Message *msg ), ( queue, msg ), @@ -135,7 +134,6 @@ IMPLEMENT_CALLBACK( ScriptMsgListener, onMessageObjectReceived, bool, ( const ch bool ScriptMsgListener::onMessageObjectReceived(StringTableEntry queue, Message *msg) { return onMessageObjectReceived_callback(queue, msg); - //return dAtob(Con::executef(this, "onMessageObjectReceived", queue, Con::getIntArg(msg->getId()))); } //----------------------------------------------------------------------------- @@ -150,7 +148,6 @@ IMPLEMENT_CALLBACK( ScriptMsgListener, onAddToQueue, void, ( const char* queue), void ScriptMsgListener::onAddToQueue(StringTableEntry queue) { - //Con::executef(this, "onAddToQueue", queue); onAddToQueue_callback(queue); IMLParent::onAddToQueue(queue); } @@ -176,7 +173,6 @@ IMPLEMENT_CALLBACK( ScriptMsgListener, onRemoveFromQueue, void, ( const char* qu void ScriptMsgListener::onRemoveFromQueue(StringTableEntry queue) { - //Con::executef(this, "onRemoveFromQueue", queue); onRemoveFromQueue_callback(queue); IMLParent::onRemoveFromQueue(queue); } From 666065ce6c59b917700c0c606843beb424212fe4 Mon Sep 17 00:00:00 2001 From: OTHGMars Date: Sat, 25 Sep 2021 02:48:30 -0400 Subject: [PATCH 10/10] GuiGameListMenuCtrl Update Methods to simplify managing localized option lists. Adds a text key to each option in an option row. Adds methods for getting the current selections index or key value. Adds methods for setting the current selection by index or key value. This change is backward compatible. If the new methods are not called, the control behavior is unaffected. --- .../gui/controls/guiGameListMenuCtrl.cpp | 144 +++++++++++++++++- .../source/gui/controls/guiGameListMenuCtrl.h | 54 ++++++- 2 files changed, 189 insertions(+), 9 deletions(-) diff --git a/Engine/source/gui/controls/guiGameListMenuCtrl.cpp b/Engine/source/gui/controls/guiGameListMenuCtrl.cpp index 61833a8d0..72b3d02f2 100644 --- a/Engine/source/gui/controls/guiGameListMenuCtrl.cpp +++ b/Engine/source/gui/controls/guiGameListMenuCtrl.cpp @@ -237,7 +237,7 @@ void GuiGameListMenuCtrl::onRenderListOption(Row* row, Point2I currentOffset) // calculate text to be at the center between the arrows GFont* font = profile->mFont; - StringTableEntry text = row->mOptions[row->mSelectedOption]; + StringTableEntry text = row->mOptions[row->mSelectedOption].mDisplayText; S32 textWidth = font->getStrWidth(text); S32 columnWidth = profile->mHitAreaLowerRight.x * xScale - profile->mRightPad - columnSplit; S32 columnCenter = columnSplit + (columnWidth >> 1); @@ -489,15 +489,18 @@ void GuiGameListMenuCtrl::addRow(const char* label, const char* optionsList, boo { static StringTableEntry DELIM = StringTable->insert("\t", true); Row* row = new Row(); - Vector options(__FILE__, __LINE__); + Vector options(__FILE__, __LINE__); S32 defaultOption = 0; S32 count = StringUnit::getUnitCount(optionsList, DELIM); for (S32 i = 0; i < count; ++i) { + OptionEntry e; const char* option = StringUnit::getUnit(optionsList, i, DELIM); - options.push_back(StringTable->insert(option, true)); + e.mDisplayText = StringTable->insert(option, true); + e.mKeyString = e.mDisplayText; + options.push_back(e); if (String::compare(option, defaultValue) == 0) defaultOption = options.size() - 1; @@ -1075,12 +1078,38 @@ StringTableEntry GuiGameListMenuCtrl::getCurrentOption(S32 rowIndex) const Row* row = (Row*)mRows[rowIndex]; if (row->mSelectedOption != NO_OPTION) { - return row->mOptions[row->mSelectedOption]; + return row->mOptions[row->mSelectedOption].mDisplayText; } } return StringTable->insert("", false); } +StringTableEntry GuiGameListMenuCtrl::getCurrentOptionKey(S32 rowIndex) const +{ + if (isValidRowIndex(rowIndex)) + { + Row* row = (Row*)mRows[rowIndex]; + if (row->mSelectedOption != NO_OPTION) + { + return row->mOptions[row->mSelectedOption].mKeyString; + } + } + return StringTable->insert("", false); +} + +S32 GuiGameListMenuCtrl::getCurrentOptionIndex(S32 rowIndex) const +{ + if (isValidRowIndex(rowIndex)) + { + Row* row = (Row*)mRows[rowIndex]; + if (row->mSelectedOption != NO_OPTION) + { + return row->mSelectedOption; + } + } + return S32(-1); +} + bool GuiGameListMenuCtrl::selectOption(S32 rowIndex, const char* theOption) { if (!isValidRowIndex(rowIndex)) @@ -1090,9 +1119,9 @@ bool GuiGameListMenuCtrl::selectOption(S32 rowIndex, const char* theOption) Row* row = (Row*)mRows[rowIndex]; - for (Vector::iterator anOption = row->mOptions.begin(); anOption < row->mOptions.end(); ++anOption) + for (Vector::iterator anOption = row->mOptions.begin(); anOption < row->mOptions.end(); ++anOption) { - if (String::compare(*anOption, theOption) == 0) + if (String::compare((*anOption).mDisplayText, theOption) == 0) { S32 newIndex = anOption - row->mOptions.begin(); row->mSelectedOption = newIndex; @@ -1103,6 +1132,45 @@ bool GuiGameListMenuCtrl::selectOption(S32 rowIndex, const char* theOption) return false; } +bool GuiGameListMenuCtrl::selectOptionByKey(S32 rowIndex, const char* optionKey) +{ + if (!isValidRowIndex(rowIndex)) + { + return false; + } + + Row* row = (Row*)mRows[rowIndex]; + + for (Vector::iterator anOption = row->mOptions.begin(); anOption < row->mOptions.end(); ++anOption) + { + if (String::compare((*anOption).mKeyString, optionKey) == 0) + { + S32 newIndex = anOption - row->mOptions.begin(); + row->mSelectedOption = newIndex; + return true; + } + } + + return false; +} + +bool GuiGameListMenuCtrl::selectOptionByIndex(S32 rowIndex, S32 optionIndex) +{ + if (!isValidRowIndex(rowIndex) || (optionIndex < 0)) + { + return false; + } + + Row* row = (Row*)mRows[rowIndex]; + if (optionIndex < row->mOptions.size()) + { + row->mSelectedOption = optionIndex; + return true; + } + + return false; +} + void GuiGameListMenuCtrl::setOptions(S32 rowIndex, const char* optionsList) { static StringTableEntry DELIM = StringTable->insert("\t", true); @@ -1119,7 +1187,10 @@ void GuiGameListMenuCtrl::setOptions(S32 rowIndex, const char* optionsList) for (S32 i = 0; i < count; ++i) { const char* option = StringUnit::getUnit(optionsList, i, DELIM); - row->mOptions[i] = StringTable->insert(option, true); + OptionEntry e; + e.mDisplayText = StringTable->insert(option, true); + e.mKeyString = e.mDisplayText; + row->mOptions[i] = e; } if (row->mSelectedOption >= row->mOptions.size()) @@ -1128,6 +1199,21 @@ void GuiGameListMenuCtrl::setOptions(S32 rowIndex, const char* optionsList) } } +void GuiGameListMenuCtrl::addOption(S32 rowIndex, const char* displayText, const char* keyText) +{ + if (!isValidRowIndex(rowIndex)) + { + return; + } + + OptionEntry e; + e.mDisplayText = StringTable->insert(displayText, true); + e.mKeyString = (keyText[0] == '\0') ? e.mDisplayText : StringTable->insert(keyText, true); + + Row* row = (Row*)mRows[rowIndex]; + row->mOptions.push_back(e); +} + void GuiGameListMenuCtrl::clickOption(Row* row, S32 xPos) { GuiGameListMenuProfile* profile = (GuiGameListMenuProfile*)mProfile; @@ -1566,6 +1652,22 @@ DefineEngineMethod(GuiGameListMenuCtrl, getCurrentOption, const char*, (S32 row) return object->getCurrentOption(row); } +DefineEngineMethod(GuiGameListMenuCtrl, getCurrentOptionKey, const char*, (S32 row), , + "Gets the key string for the currently selected option of the given row.\n\n" + "@param row Index of the row to get the option from.\n" + "@return The key (or id) that was assigned to the selected option on the given row. If there is no selected option then the empty string is returned.") +{ + return object->getCurrentOptionKey(row); +} + +DefineEngineMethod(GuiGameListMenuCtrl, getCurrentOptionIndex, S32, (S32 row), , + "Gets the index into the option list for the currently selected option of the given row.\n\n" + "@param row Index of the row to get the option from.\n" + "@return The index of the selected option on the given row. If there is no selected option then -1 is returned.") +{ + return object->getCurrentOptionIndex(row); +} + DefineEngineMethod(GuiGameListMenuCtrl, selectOption, bool, (S32 row, const char* option), , "Set the row's current option to the one specified\n\n" "@param row Index of the row to set an option on.\n" @@ -1575,6 +1677,24 @@ DefineEngineMethod(GuiGameListMenuCtrl, selectOption, bool, (S32 row, const char return object->selectOption(row, option); } +DefineEngineMethod(GuiGameListMenuCtrl, selectOptionByKey, bool, (S32 row, const char* optionKey), , + "Set the row's current option to the one with the specified key.\n\n" + "@param row Index of the row to set an option on.\n" + "@param optionKey The key string that was assigned to the option to be made active.\n" + "@return True if the row contained the key and the option and was set, false otherwise.") +{ + return object->selectOptionByKey(row, optionKey); +} + +DefineEngineMethod(GuiGameListMenuCtrl, selectOptionByIndex, bool, (S32 row, S32 optionIndex), , + "Set the row's current option to the one at the specified index.\n\n" + "@param row Index of the row to set an option on.\n" + "@param optionIndex The index of the option to be made active.\n" + "@return True if the index was valid and the option and was set, false otherwise.") +{ + return object->selectOptionByIndex(row, optionIndex); +} + DefineEngineMethod(GuiGameListMenuCtrl, setOptions, void, (S32 row, const char* optionsList), , "Sets the list of options on the given row.\n\n" "@param row Index of the row to set options on." @@ -1583,6 +1703,16 @@ DefineEngineMethod(GuiGameListMenuCtrl, setOptions, void, (S32 row, const char* object->setOptions(row, optionsList); } +DefineEngineMethod(GuiGameListMenuCtrl, addOption, void, (S32 row, const char* displayText, const char* keyText), (""), + "Adds an option to the list of options on the given row.\n\n" + "@param row Index of the row to add the option on.\n" + "@param displayText The text to display for this option.\n" + "@param keyText [Optional] The id string to associate with this value. " + "If unset, the id will be the same as the display text.\n") +{ + object->addOption(row, displayText, keyText); +} + DefineEngineMethod(GuiGameListMenuCtrl, getValue, F32, (S32 row), , "Sets the list of options on the given row.\n\n" "@param row Index of the row to set options on." diff --git a/Engine/source/gui/controls/guiGameListMenuCtrl.h b/Engine/source/gui/controls/guiGameListMenuCtrl.h index 15bbf49a3..3e6c3c7b0 100644 --- a/Engine/source/gui/controls/guiGameListMenuCtrl.h +++ b/Engine/source/gui/controls/guiGameListMenuCtrl.h @@ -37,6 +37,18 @@ public: typedef GuiGameListMenuProfile Profile; protected: + + /// \struct OptionEntry + /// Display text and ID key for each entry in an option row. + struct OptionEntry + { + StringTableEntry mDisplayText; ///< The text that is displayed for the option + StringTableEntry mKeyString; ///< Key value that is associated with this option + OptionEntry() : mDisplayText(StringTable->EmptyString()), mKeyString(StringTable->EmptyString()) {} + virtual ~OptionEntry() {} + }; + + /// \struct Row /// Internal data representation of a single row in the control. struct Row @@ -60,7 +72,7 @@ protected: Mode mMode; //List options - Vector mOptions; ///< Collection of options available to display + Vector mOptions; ///< Collection of options available to display S32 mSelectedOption; ///< Index into mOptions pointing at the selected option bool mWrapOptions; ///< Determines if options should "wrap around" at the ends @@ -174,13 +186,43 @@ public: /// string is returned. StringTableEntry getCurrentOption(S32 rowIndex) const; + /// Gets the key string for the currently selected option of the given row + /// + /// \param rowIndex Index of the row to get the option from. + /// \return The key (or id) that was assigned to the selected option on the + /// given row. If there is no selected option then the empty string is returned. + StringTableEntry getCurrentOptionKey(S32 rowIndex) const; + + /// Gets the index into the option list for the currently selected option of the given row. + /// + /// \param rowIndex Index of the row to get the option from. + /// \return The index of the selected option on the given row. If there is no + /// selected option then -1 is returned. + S32 getCurrentOptionIndex(S32 rowIndex) const; + /// Attempts to set the given row to the specified selected option. The option /// will only be set if the option exists in the control. /// /// \param rowIndex Index of the row to set an option on. /// \param option The option to be made active. /// \return True if the row contained the option and was set, false otherwise. - bool selectOption(S32 rowIndex, StringTableEntry option); + bool selectOption(S32 rowIndex, const char* option); + + /// Attempts to set the given row to the option with the specified key. The + /// option will only be set if the key exists in the control. + /// + /// \param rowIndex Index of the row to set an option on. + /// \param optionKey The key string that was assigned to the option to be made active. + /// \return True if the row contained the key and the option and was set, false otherwise. + bool selectOptionByKey(S32 rowIndex, const char* optionKey); + + /// Attempts to set the given row to the option at the specified index. The option + /// will only be set if the index is valid. + /// + /// \param rowIndex Index of the row to set an option on. + /// \param optionIndex The index of the option to be made active. + /// \return True if the index was valid and the option and was set, false otherwise. + bool selectOptionByIndex(S32 rowIndex, S32 optionIndex); /// Sets the list of options on the given row. /// @@ -188,6 +230,14 @@ public: /// \param optionsList A tab separated list of options for the control. void setOptions(S32 rowIndex, const char* optionsList); + /// Adds an option to the list of options on the given row. + /// + /// \param rowIndex Index of the row to set options on. + /// \param displayText The text to display for this option. + /// \param keyText The id string to associate with this value. If NULL the + /// id will be the same as the display text. + void addOption(S32 rowIndex, const char* displayText, const char* keyText); + /// Activates the current row. The script callback of the current row will /// be called (if it has one). virtual void activateRow();