From dbe465bbcca910014511bb4f63deaf84c09cc356 Mon Sep 17 00:00:00 2001 From: Stentorious <124759154+Stentorious@users.noreply.github.com> Date: Thu, 19 Feb 2026 05:06:15 -0500 Subject: [PATCH 1/2] Add Get/SetStringVariable functions --- nvse/nvse/CommandTable.cpp | 2 + nvse/nvse/Commands_Script.cpp | 119 +++++++++++++++++++++++++++------- nvse/nvse/Commands_Script.h | 17 +++-- 3 files changed, 111 insertions(+), 27 deletions(-) diff --git a/nvse/nvse/CommandTable.cpp b/nvse/nvse/CommandTable.cpp index 6b43fe51..66604f23 100644 --- a/nvse/nvse/CommandTable.cpp +++ b/nvse/nvse/CommandTable.cpp @@ -2270,6 +2270,8 @@ void CommandTable::AddCommandsV6() // 6.4 beta 06 ADD_CMD_RET(GetPressedKeys, kRetnType_Array); + ADD_CMD_RET(GetStringVariable, kRetnType_String); + ADD_CMD(SetStringVariable); } diff --git a/nvse/nvse/Commands_Script.cpp b/nvse/nvse/Commands_Script.cpp index 65e41421..5fdac2b0 100644 --- a/nvse/nvse/Commands_Script.cpp +++ b/nvse/nvse/Commands_Script.cpp @@ -237,6 +237,7 @@ enum eScriptVar_Get = 1, eScriptVar_GetRef, eScriptVar_Has, + eScriptVar_GetStr, }; bool GetVariable_Execute(COMMAND_ARGS, UInt32 whichAction) @@ -268,22 +269,42 @@ bool GetVariable_Execute(COMMAND_ARGS, UInt32 whichAction) if (targetScript && targetEventList) { VariableInfo *varInfo = targetScript->GetVariableByName(varName); + + if (whichAction == eScriptVar_Has) + { + *result = varInfo ? 1 : 0; + return true; + } + + // String handling + if (whichAction == eScriptVar_GetStr) + { + const char* strValue = nullptr; + if (varInfo) + { + if (ScriptLocal* var = targetEventList->GetVariable(varInfo->idx)) + { + StringVar *strVar = g_StringMap.Get(static_cast(var->data)); + if (strVar) + strValue = strVar->GetCString(); + } + } + AssignToStringVar(PASS_COMMAND_ARGS, strValue); + return true; + } + + // Numeric/Ref handling if (varInfo) { - if (whichAction == eScriptVar_Has) - *result = 1; - else + ScriptLocal *var = targetEventList->GetVariable(varInfo->idx); + if (var) { - ScriptLocal *var = targetEventList->GetVariable(varInfo->idx); - if (var) + if (whichAction == eScriptVar_Get) + *result = var->data; + else if (whichAction == eScriptVar_GetRef) { - if (whichAction == eScriptVar_Get) - *result = var->data; - else if (whichAction == eScriptVar_GetRef) - { - const auto refResult = (UInt32 *)result; - *refResult = (*(UInt64 *)&var->data); - } + const auto refResult = (UInt32 *)result; + *refResult = (*(UInt64 *)&var->data); } } } @@ -377,6 +398,52 @@ bool Cmd_SetRefVariable_Execute(COMMAND_ARGS) return true; } +bool Cmd_SetStringVariable_Execute(COMMAND_ARGS) +{ + char varName[256]; + char newValue[kMaxMessageLength]; + TESQuest *quest = nullptr; + Script *targetScript = nullptr; + ScriptEventList *targetEventList = nullptr; + float value = 0; + *result = 0; + + if (!ExtractArgs(EXTRACT_ARGS, &varName, &newValue, &quest)) + return true; + if (quest) + { + const auto scriptable = DYNAMIC_CAST(quest, TESQuest, TESScriptableForm); + targetScript = scriptable->script; + targetEventList = quest->scriptEventList; + } + else if (thisObj) + { + const auto scriptable = DYNAMIC_CAST(thisObj->baseForm, TESForm, TESScriptableForm); + if (scriptable) + { + targetScript = scriptable->script; + targetEventList = thisObj->GetEventList(); + } + } + + if (targetScript && targetEventList) + { + VariableInfo *varInfo = targetScript->GetVariableByName(varName); + if (varInfo) + { + ScriptLocal *var = targetEventList->GetVariable(varInfo->idx); + if (var) + { + StringVar *strVar = g_StringMap.Get(static_cast(var->data)); + if (strVar) + strVar->Set(newValue); + } + } + } + + return true; +} + bool Cmd_HasVariable_Execute(COMMAND_ARGS) { GetVariable_Execute(PASS_COMMAND_ARGS, eScriptVar_Has); @@ -407,6 +474,12 @@ bool Cmd_GetArrayVariable_Execute(COMMAND_ARGS) return true; } +bool Cmd_GetStringVariable_Execute(COMMAND_ARGS) +{ + GetVariable_Execute(PASS_COMMAND_ARGS, eScriptVar_GetStr); + return true; +} + bool Cmd_CompareScripts_Execute(COMMAND_ARGS) { Script *script1 = nullptr; @@ -601,7 +674,7 @@ bool ExtractEventCallback(ExpressionEvaluator &eval, EventManager::EventCallback // any filters? Could also be priority for (auto i = 2; i < eval.NumArgs(); i++) { - if (const TokenPair* pair = eval.Arg(i)->GetPair(); + if (const TokenPair* pair = eval.Arg(i)->GetPair(); pair && pair->left && pair->right) [[likely]] { if (pair->left->CanConvertTo(kTokenType_String)) @@ -711,7 +784,7 @@ bool ProcessEventHandler(std::string &eventName, EventManager::EventCallback &ca { if (priority != EventManager::kHandlerPriority_Default && priority != EventManager::kHandlerPriority_Invalid) [[unlikely]] { - ShowRuntimeScriptError(callback.TryGetScript(), &eval, "Cannot use non-default (non-%i) and non-invalid (non-%i) priority %i for removing an LN event.", + ShowRuntimeScriptError(callback.TryGetScript(), &eval, "Cannot use non-default (non-%i) and non-invalid (non-%i) priority %i for removing an LN event.", EventManager::kHandlerPriority_Default, EventManager::kHandlerPriority_Invalid, priority); return false; } @@ -908,12 +981,12 @@ bool Cmd_DumpEventHandlers_Execute(COMMAND_ARGS) auto const DumpHandlerInfo = [&, thisObj, scriptFilter](int priority, const EventManager::EventCallback& handler) { - if ((!scriptFilter || scriptFilter == handler.TryGetScript()) - && !handler.IsRemoved() + if ((!scriptFilter || scriptFilter == handler.TryGetScript()) + && !handler.IsRemoved() && (argsToFilter->empty() || handler.DoNewFiltersMatch(thisObj, argsToFilter, accurateArgTypes, info, &eval))) { - std::string const toPrint = FormatString(">> Priority: %i, handler: %s, filters: %s", - priority, + std::string const toPrint = FormatString(">> Priority: %i, handler: %s, filters: %s", + priority, handler.GetCallbackFuncAsStr().c_str(), handler.GetFiltersAsStr().c_str()); Console_Print_Str(toPrint); @@ -997,9 +1070,9 @@ bool Cmd_GetEventHandlers_Execute(COMMAND_ARGS) auto const accurateArgTypes = info.HasUnknownArgTypes() ? argTypes : info.GetArgTypesAsStackVector(); - auto const TryAddHandlerToArray = + auto const TryAddHandlerToArray = [&, scriptFilter, thisObj, scriptObj] - (EventManager::CallbackMap::const_iterator i, + (EventManager::CallbackMap::const_iterator i, int handlerPos, ArrayVar* arrOfHandlers) { @@ -1012,7 +1085,7 @@ bool Cmd_GetEventHandlers_Execute(COMMAND_ARGS) } }; - if (priorityFilter != EventManager::kHandlerPriority_Invalid) + if (priorityFilter != EventManager::kHandlerPriority_Invalid) { // filter by priority auto const priorityRange = info.callbacks.equal_range(priorityFilter); @@ -1607,7 +1680,7 @@ bool Cmd_DumpCommandWikiDocs_Execute(COMMAND_ARGS) if (IsConsoleMode()) Console_Print("Dumping wiki-style documentation for functions in opcode range."); - std::for_each(commandInfoVec.begin(), commandInfoVec.end(), + std::for_each(commandInfoVec.begin(), commandInfoVec.end(), [&versionNumBuf](CommandInfo* commandInfo) {commandInfo->DumpWikiDocs(versionNumBuf); }); if (IsConsoleMode()) @@ -1679,7 +1752,7 @@ class TernaryEvaluator return true; } - ScriptToken* GetResult() const + ScriptToken* GetResult() const { return m_eval.m_args[1]; } diff --git a/nvse/nvse/Commands_Script.h b/nvse/nvse/Commands_Script.h index 9e9b85bf..2801959f 100644 --- a/nvse/nvse/Commands_Script.h +++ b/nvse/nvse/Commands_Script.h @@ -27,6 +27,7 @@ DEFINE_COMMAND(GetVariable, looks up the value of a variable by name, 0, 2, kPar DEFINE_COMMAND(HasVariable, returns true if the script has a variable with the specified name, 0, 2, kParams_GetVariable); DEFINE_COMMAND(GetRefVariable, looks up the value of a ref variable by name, 0, 2, kParams_GetVariable); DEFINE_CMD_ALT(GetArrayVariable, GetArrayVar, looks up an array variable by name on the calling object or specified quest, 0, 2, kParams_GetVariable); +DEFINE_COMMAND(GetStringVariable, looks up a string variable by name on the calling object or specified quest, 0, 2, kParams_GetVariable); static ParamInfo kParams_SetNumVariable[3] = { @@ -42,8 +43,16 @@ static ParamInfo kParams_SetRefVariable[3] = { "quest", kParamType_Quest, 1 }, }; +static ParamInfo kParams_SetStrVariable[3] = +{ + { "variable name", kParamType_String, 0 }, + { "variable value", kParamType_String, 0 }, + { "quest", kParamType_Quest, 1 }, +}; + DEFINE_COMMAND(SetVariable, sets the value of a variable by name, 0, 3, kParams_SetNumVariable); DEFINE_COMMAND(SetRefVariable, sets the value of a variable by name, 0, 3, kParams_SetRefVariable); +DEFINE_COMMAND(SetStringVariable, sets the value of a variable by name, 0, 3, kParams_SetStrVariable); static ParamInfo kParams_CompareScripts[2] = { @@ -115,7 +124,7 @@ DEFINE_COMMAND_EXP(SetEventHandler, "defines a function script to serve as a cal DEFINE_COMMAND_EXP(SetEventHandlerAlt, "Uses the new event filtering system.", 0, kNVSEParams_SetEventHandlerAlt); -DEFINE_COMMAND_EXP(RemoveEventHandler, "removes event handlers matching the event, script, and optional filters specified", +DEFINE_COMMAND_EXP(RemoveEventHandler, "removes event handlers matching the event, script, and optional filters specified", 0, kNVSEParams_SetEventHandler); DEFINE_CMD(GetCurrentEventName, returns the name of the event currently being processed by an event handler, 0, NULL); @@ -453,9 +462,9 @@ static ParamInfo kParams_HasScriptCommand[3] = DEFINE_COMMAND(DecompileScript, decompiles a script to file, false, 2, kParams_OneForm_OneOptionalString); DEFINE_COMMAND(HasScriptCommand, returns 1 if script contains call to a command, false, 3, kParams_HasScriptCommand); DEFINE_COMMAND(GetCommandOpcode, gets opcode for command name, false, 1, kParams_OneString); -DEFINE_CMD_ALIAS(DumpCommandWikiDoc, DumpWikiDoc, dumps wiki-style documentation for a command, +DEFINE_CMD_ALIAS(DumpCommandWikiDoc, DumpWikiDoc, dumps wiki-style documentation for a command, false, kParams_OneString); -DEFINE_CMD_ALIAS(DumpCommandWikiDocs, DumpWikiDocs, dumps wiki-style documentation for multiple commands, +DEFINE_CMD_ALIAS(DumpCommandWikiDocs, DumpWikiDocs, dumps wiki-style documentation for multiple commands, false, kParams_TwoInts_OneOptionalString); @@ -484,7 +493,7 @@ static ParamInfo kNVSEParams_OneString_OneOptionalBool[] = { "bool", kNVSEParamType_Boolean, 1 }, }; -DEFINE_CMD_ALT_EXP(CompileScript, GetUDFFromFile, "Returns a compiled script from a file, to call as a UDF.", +DEFINE_CMD_ALT_EXP(CompileScript, GetUDFFromFile, "Returns a compiled script from a file, to call as a UDF.", false, kNVSEParams_OneString_OneOptionalBool); static ParamInfo kNVSEParams_MatchesAnyOf[] = From ff447fd2b9c53ea572cc619ef7a4466bf6dd3bd0 Mon Sep 17 00:00:00 2001 From: Stentorious <124759154+Stentorious@users.noreply.github.com> Date: Thu, 19 Feb 2026 13:50:57 -0500 Subject: [PATCH 2/2] Restructure, return empty string on invalid variable --- nvse/nvse/Commands_Script.cpp | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/nvse/nvse/Commands_Script.cpp b/nvse/nvse/Commands_Script.cpp index 5fdac2b0..5ccd90e2 100644 --- a/nvse/nvse/Commands_Script.cpp +++ b/nvse/nvse/Commands_Script.cpp @@ -276,24 +276,6 @@ bool GetVariable_Execute(COMMAND_ARGS, UInt32 whichAction) return true; } - // String handling - if (whichAction == eScriptVar_GetStr) - { - const char* strValue = nullptr; - if (varInfo) - { - if (ScriptLocal* var = targetEventList->GetVariable(varInfo->idx)) - { - StringVar *strVar = g_StringMap.Get(static_cast(var->data)); - if (strVar) - strValue = strVar->GetCString(); - } - } - AssignToStringVar(PASS_COMMAND_ARGS, strValue); - return true; - } - - // Numeric/Ref handling if (varInfo) { ScriptLocal *var = targetEventList->GetVariable(varInfo->idx); @@ -306,8 +288,17 @@ bool GetVariable_Execute(COMMAND_ARGS, UInt32 whichAction) const auto refResult = (UInt32 *)result; *refResult = (*(UInt64 *)&var->data); } + else if (whichAction == eScriptVar_GetStr) + { + StringVar *strVar = g_StringMap.Get(static_cast(var->data)); + AssignToStringVar(PASS_COMMAND_ARGS, strVar ? strVar->GetCString() : ""); + return true; + } } } + + if (whichAction == eScriptVar_GetStr) + AssignToStringVar(PASS_COMMAND_ARGS, ""); } return true;