From a95f26a02dd1e823ffd7eba4b4ec725fb4472d24 Mon Sep 17 00:00:00 2001 From: NovaRain Date: Mon, 21 Jan 2019 09:56:06 +0800 Subject: [PATCH] Fixed set_self function for use_obj_on_obj, attack, attack_complex vanilla functions (from Mr.Stalin) (#214) --- artifacts/scripting/sfall function notes.txt | 3 +- sfall/Modules/ScriptExtender.cpp | 64 ++++++++++++++------ 2 files changed, 49 insertions(+), 18 deletions(-) diff --git a/artifacts/scripting/sfall function notes.txt b/artifacts/scripting/sfall function notes.txt index ff4374c7..3d987ed8 100644 --- a/artifacts/scripting/sfall function notes.txt +++ b/artifacts/scripting/sfall function notes.txt @@ -163,8 +163,9 @@ array - array ID to be used with array-related functions (actually an integer) - overrides the scripts self_obj for the next function call. - It is primarily used to allow the calling of functions which take an implicit self_obj parameter (e.g. drop_obj) from global scripts, but it can also be used from normal scripts; - self_obj will revert back to its original value after the next function call. -- calling self_obj(0) will also revert self_obj to original value +- calling self_obj(0) will also revert self_obj to original value. It is recommended to call this after each use of set_self in normal scripts in order to avoid unforeseen side effects. - source_obj, target_obj, and similar functions will not work if preceded by "set_self" +- NOTE: for use_obj_on_obj vanilla function to work correctly, it is required to call set_self twice. > void mod_skill_points_per_level(int x) - accepts a value of between -100 and 100, and modifies the number of skill points the player receives when they level up. diff --git a/sfall/Modules/ScriptExtender.cpp b/sfall/Modules/ScriptExtender.cpp index 4e518158..d0b22695 100644 --- a/sfall/Modules/ScriptExtender.cpp +++ b/sfall/Modules/ScriptExtender.cpp @@ -72,6 +72,16 @@ struct ExportedVar { ExportedVar() : val(0), type(VAR_TYPE_INT) {} }; +struct SelfOverrideObj { + fo::GameObject* object; + char counter; + + bool UnSetSelf() { + if (counter) counter--; + return counter == 0; + } +}; + static std::vector globalScriptPathList; static std::map globalScriptFilesList; @@ -83,7 +93,7 @@ typedef std::unordered_map SfallProgsMap; static SfallProgsMap sfallProgsMap; // a map scriptPtr => self_obj to override self_obj for all script types using set_self -std::unordered_map selfOverrideMap; +std::unordered_map selfOverrideMap; typedef std::unordered_map ExportedVarsMap; static ExportedVarsMap globalExportedVars; @@ -98,23 +108,24 @@ DWORD availableGlobalScriptTypes = 0; bool isGameLoading; bool alwaysFindScripts; -fo::ScriptInstance overrideScriptStruct; +fo::ScriptInstance overrideScriptStruct = {0}; static const DWORD scr_ptr_back = fo::funcoffs::scr_ptr_ + 5; static const DWORD scr_find_sid_from_program = fo::funcoffs::scr_find_sid_from_program_ + 5; static const DWORD scr_find_obj_from_program = fo::funcoffs::scr_find_obj_from_program_ + 7; static DWORD _stdcall FindSid(fo::Program* script) { - std::unordered_map::iterator overrideIt = selfOverrideMap.find(script); + std::unordered_map::iterator overrideIt = selfOverrideMap.find(script); if (overrideIt != selfOverrideMap.end()) { - DWORD scriptId = overrideIt->second->scriptId; + DWORD scriptId = overrideIt->second.object->scriptId; // script + overrideScriptStruct.id = scriptId; if (scriptId != -1) { - selfOverrideMap.erase(overrideIt); + if (overrideIt->second.UnSetSelf()) selfOverrideMap.erase(overrideIt); return scriptId; // returns the real scriptId of object if it is scripted } - overrideScriptStruct.selfObject = overrideIt->second; - overrideScriptStruct.targetObject = overrideIt->second; - selfOverrideMap.erase(overrideIt); // this reverts self_obj back to original value for next function calls + overrideScriptStruct.selfObject = overrideIt->second.object; + overrideScriptStruct.targetObject = overrideIt->second.object; + if (overrideIt->second.UnSetSelf()) selfOverrideMap.erase(overrideIt); // this reverts self_obj back to original value for next function calls return -2; // override struct } // this will allow to use functions like roll_vs_skill, etc without calling set_self (they don't really need self object) @@ -142,12 +153,16 @@ static void __declspec(naked) FindSidHack() { retn; override_script: test edx, edx; - jz end; + jz skip; add esp, 4; lea eax, overrideScriptStruct; mov [edx], eax; mov eax, -2; retn; +skip: + add esp, 4; + dec eax; // set -3; + retn; end: pop eax; push ebx; @@ -162,7 +177,15 @@ end: static void __declspec(naked) ScrPtrHack() { __asm { cmp eax, -2; - jnz end; + jnz skip; + xor eax, eax; + retn; +skip: + cmp eax, -3; + jne end; + lea eax, overrideScriptStruct; + mov [edx], eax; + mov esi, [eax]; // script.id xor eax, eax; retn; end: @@ -360,13 +383,21 @@ DWORD _stdcall GetGlobalVarInt(DWORD var) { } void _stdcall SetSelfObject(fo::Program* script, fo::GameObject* obj) { + std::unordered_map::iterator it = selfOverrideMap.find(script); + bool isFind = (it != selfOverrideMap.end()); if (obj) { - selfOverrideMap[script] = obj; - } else { - std::unordered_map::iterator it = selfOverrideMap.find(script); - if (it != selfOverrideMap.end()) { - selfOverrideMap.erase(it); + if (isFind) + if (it->second.object == obj) + it->second.counter = 2; + else { + it->second.object = obj; + it->second.counter = 0; + } + else { + selfOverrideMap[script] = {obj, 0}; } + } else { + if (isFind) selfOverrideMap.erase(it); } } @@ -548,7 +579,7 @@ static void RunScript(GlobalScript* script) { - reset reg_anim_* combatstate checks */ static void ResetStateAfterFrame() { - if (tempArrays.size()) { + if (!tempArrays.empty()) { for (std::set::iterator it = tempArrays.begin(); it != tempArrays.end(); ++it) FreeArray(*it); tempArrays.clear(); @@ -702,7 +733,6 @@ void ScriptExtender::init() { MakeJump(0x4A390C, FindSidHack); MakeJump(0x4A5E34, ScrPtrHack); - memset(&overrideScriptStruct, 0, sizeof(fo::ScriptInstance)); MakeJump(0x4A67F0, ExecMapScriptsHack);