diff --git a/artifacts/scripting/headers/sfall.h b/artifacts/scripting/headers/sfall.h index 2f6e1927..b684946f 100644 --- a/artifacts/scripting/headers/sfall.h +++ b/artifacts/scripting/headers/sfall.h @@ -62,6 +62,7 @@ #define HOOK_SUBCOMBATDAMAGE (37) #define HOOK_SETLIGHTING (38) #define HOOK_SNEAK (39) +#define HOOK_STDPROCEDURE (40) //Valid arguments to list_begin #define LIST_CRITTERS (0) diff --git a/artifacts/scripting/hookscripts.txt b/artifacts/scripting/hookscripts.txt index 4c76758c..816fc29a 100644 --- a/artifacts/scripting/hookscripts.txt +++ b/artifacts/scripting/hookscripts.txt @@ -10,8 +10,6 @@ To aid in mods compatibility, avoid using hs_xxx .int scripts. Instead it is rec Example setup for a hook-script based mod: - - procedure tohit_hook_handler begin display_msg("Modifying hit_hook " + get_sfall_arg); set_hit_chance_max(100); @@ -25,8 +23,6 @@ procedure start begin end - - There are script functions available, specific to hook scripts: > int init_hook() @@ -48,7 +44,7 @@ Changes argument value. The argument number (argnum) is 0-indexed. This is usefu Used from a normal global script if you want to run it at the same point a full hook script would normally run. In case of this function, "start" proc will be executed in a current global script. You can use all above functions like normal. > void register_hook_proc(int hook, procedure proc) -The same as register_hook, except that you specifically define which procedure in the current script should be called as a hook (instead of "start"). Pass procedure the same as how you use dialog option functions. This IS the recommended way to use hook scripts, as it gives both modularity (each mod logic in a separate global script, no conflicts if you don't use "hs_*.int" scripts) and flexibility (you can place all related hook scripts for specific mod in a single script!). +The same as register_hook, except that you specifically define which procedure in the current script should be called as a hook (instead of "start" by default). Pass procedure the same as how you use dialog option functions. This IS the recommended way to use hook scripts, as it gives both modularity (each mod logic in a separate global script, no conflicts if you don't use "hs_*.int" scripts) and flexibility (you can place all related hook scripts for specific mod in a single script!). NOTE: you can hook several scripts to a single hook point, for example if it's different mods from different authors or just some different aspects of one larger mod. In this case scripts are executed in reverse order of how they were registered. When one of the scripts in a chain returns value with "set_sfall_return", the next script may override this value if calls "set_sfall_return" again. Sometimes you need to multiply certain value in a chain of hook scripts. Example: let's say we have a Mod A which reduces all "to hit" chances by 50%. The code might look like this: @@ -336,7 +332,6 @@ int arg4 - Type of hook (0 - when subtracting ammo after single shot attack, int ret1 - new ammo cost value (set to 0 for unlimited ammo) - ------------------------------------------- HOOK_KEYPRESS (hs_keypress.int) @@ -633,3 +628,15 @@ Critter arg3 - the critter (usually dude_obj) int ret1 - overrides the result of the Sneak check int ret2 - overrides the duration time for the current result + +------------------------------------------- + +HOOK_STDPROCEDURE (hs_stdprocedure.int) + +Runs once every time before executing the standard procedure (handler) in the script of an object. + +int arg1 - number of the standard script handler (see define.h, except: start, critter_p_proc, and map_update_p_proc) +Obj arg2 - the object connected to the executed script (self_obj) +Obj arg3 - the object that called this handler (source_obj, can be 0) + +int ret1 - pass -1 to cancel the execution of the handler diff --git a/sfall/Modules/Criticals.cpp b/sfall/Modules/Criticals.cpp index 14579e3b..a11163dc 100644 --- a/sfall/Modules/Criticals.cpp +++ b/sfall/Modules/Criticals.cpp @@ -107,7 +107,7 @@ static void CritTableLoad() { memcpy(&baseCritTable[38], fo::var::pc_crit_succ_eff, 6 * 9 * sizeof(fo::CritInfo)); // PC crit table if (mode == 3) { - dlog(" and CriticalOverrides.ini (new fmt)", DL_CRITICALS); + dlogr(" and CriticalOverrides.ini (new fmt)", DL_CRITICALS); char buf[32], buf2[32], buf3[32]; for (int critter = 0; critter < Criticals::critTableCount; critter++) { sprintf_s(buf, "c_%02d", critter); @@ -129,8 +129,9 @@ static void CritTableLoad() { } } } + } else { + dlog("\n", DL_CRITICALS); } - dlog("\n", DL_CRITICALS); } } diff --git a/sfall/Modules/HookScripts.cpp b/sfall/Modules/HookScripts.cpp index 37ee7605..84db08dc 100644 --- a/sfall/Modules/HookScripts.cpp +++ b/sfall/Modules/HookScripts.cpp @@ -84,6 +84,7 @@ static HooksInjectInfo injectHooks[] = { {HOOK_SUBCOMBATDAMAGE, Inject_SubCombatDamageHook, false}, // replace the code logic {HOOK_SETLIGHTING, Inject_SetLightingHook, false}, {HOOK_SNEAK, Inject_SneakCheckHook, false}, + {HOOK_STDPROCEDURE, Inject_ScriptProcedureHook, false}, }; bool HookScripts::injectAllHooks; diff --git a/sfall/Modules/HookScripts.h b/sfall/Modules/HookScripts.h index 3db160ac..9836c37d 100644 --- a/sfall/Modules/HookScripts.h +++ b/sfall/Modules/HookScripts.h @@ -66,6 +66,7 @@ enum HookType HOOK_SUBCOMBATDAMAGE = 37, HOOK_SETLIGHTING = 38, HOOK_SNEAK = 39, + HOOK_STDPROCEDURE = 40, HOOK_COUNT }; diff --git a/sfall/Modules/HookScripts/Common.cpp b/sfall/Modules/HookScripts/Common.cpp index 91b06f65..102f1979 100644 --- a/sfall/Modules/HookScripts/Common.cpp +++ b/sfall/Modules/HookScripts/Common.cpp @@ -102,7 +102,7 @@ static void _stdcall RunSpecificHookScript(HookScript *hook) { if (hook->callback != -1) { fo::func::executeProcedure(hook->prog.ptr, hook->callback); } else { - RunScriptProc(&hook->prog, fo::ScriptProc::start); + hook->callback = RunScriptStartProc(&hook->prog); // run start } } diff --git a/sfall/Modules/HookScripts/ObjectHs.cpp b/sfall/Modules/HookScripts/ObjectHs.cpp index 5e3727c0..a54a8d17 100644 --- a/sfall/Modules/HookScripts/ObjectHs.cpp +++ b/sfall/Modules/HookScripts/ObjectHs.cpp @@ -235,6 +235,47 @@ skip: } } +static DWORD __fastcall StdProcedureHook_Script(long numHandler, fo::ScriptInstance* script, DWORD procTable) { + BeginHook(); + argCount = 3; + + args[0] = numHandler; + args[1] = (DWORD)script->selfObject; + args[2] = (DWORD)script->sourceObject; + RunHookScript(HOOK_STDPROCEDURE); + + if (cRet > 0) { + long retval = rets[0]; + if (retval == -1) procTable = retval; + } + EndHook(); + return procTable; +} + +static void __declspec(naked) ScriptStdProcedureHook() { + using namespace fo::ScriptProc; + __asm { + mov eax, [eax + 0x54]; // Script.procedure_table + test eax, eax; + jle end; + cmp ecx, critter_p_proc; + je skip; + cmp ecx, map_update_p_proc; + je skip; + cmp ecx, start; + jle skip; + push ecx; + push eax; // procTable + mov edx, esi; // script + call StdProcedureHook_Script; // ecx - numHandler + pop ecx; +skip: + test eax, eax; +end: + retn; + } +} + void Inject_UseObjOnHook() { HookCalls(UseObjOnHook, { 0x49C606, 0x473619 }); @@ -264,6 +305,10 @@ void Inject_SetLightingHook() { MakeCall(0x47A934, SetMapLightHook, 1); } +void Inject_ScriptProcedureHook() { + MakeCall(0x4A491F, ScriptStdProcedureHook); +} + void InitObjectHookScripts() { LoadHookScript("hs_useobjon", HOOK_USEOBJON); @@ -271,6 +316,7 @@ void InitObjectHookScripts() { LoadHookScript("hs_useanimobj", HOOK_USEANIMOBJ); LoadHookScript("hs_descriptionobj", HOOK_DESCRIPTIONOBJ); LoadHookScript("hs_setlighting", HOOK_SETLIGHTING); + LoadHookScript("hs_stdprocedure", HOOK_STDPROCEDURE); } } diff --git a/sfall/Modules/HookScripts/ObjectHs.h b/sfall/Modules/HookScripts/ObjectHs.h index 020cc066..f14084be 100644 --- a/sfall/Modules/HookScripts/ObjectHs.h +++ b/sfall/Modules/HookScripts/ObjectHs.h @@ -9,4 +9,5 @@ namespace sfall void Inject_UseAnimateObjHook(); void Inject_DescriptionObjHook(); void Inject_SetLightingHook(); + void Inject_ScriptProcedureHook(); } diff --git a/sfall/Modules/ScriptExtender.cpp b/sfall/Modules/ScriptExtender.cpp index c6bb5b66..ee1b76b0 100644 --- a/sfall/Modules/ScriptExtender.cpp +++ b/sfall/Modules/ScriptExtender.cpp @@ -57,17 +57,13 @@ std::string ScriptExtender::iniConfigFolder; struct GlobalScript { ScriptProgram prog; + int startProc; // position of the 'start' procedure in the script int count; int repeat; - int mode; //0 - local map loop, 1 - input loop, 2 - world map loop, 3 - local and world map loops + int mode; // 0 - local map loop, 1 - input loop, 2 - world map loop, 3 - local and world map loops - GlobalScript() {} - GlobalScript(ScriptProgram script) { - prog = script; - count = 0; - repeat = 0; - mode = 0; - } + //GlobalScript() {} + GlobalScript(ScriptProgram script) : prog(script), startProc(-1), count(0), repeat(0), mode(0) {} }; struct ExportedVar { @@ -433,10 +429,8 @@ void AddProgramToMap(ScriptProgram &prog) { } ScriptProgram* GetGlobalScriptProgram(fo::Program* scriptPtr) { - for (std::vector::iterator it = globalScripts.begin(); it != globalScripts.end(); it++) { - if (it->prog.ptr == scriptPtr) return &it->prog; - } - return nullptr; + SfallProgsMap::iterator it = sfallProgsMap.find(scriptPtr); + return (it == sfallProgsMap.end()) ? nullptr : &it->second ; // prog } bool _stdcall IsGameScript(const char* filename) { @@ -458,6 +452,7 @@ static void LoadGlobalScriptsList() { if (prog.ptr) { dlogr(" Done", DL_SCRIPT); GlobalScript gscript = GlobalScript(prog); + gscript.startProc = prog.procLookup[fo::ScriptProc::start]; // get 'start' procedure position globalScripts.push_back(gscript); AddProgramToMap(prog); // initialize script (start proc will be executed for the first time) -- this needs to be after script is added to "globalScripts" array @@ -553,9 +548,20 @@ void RunScriptProc(ScriptProgram* prog, long procId) { } } +int RunScriptStartProc(ScriptProgram* prog) { + fo::Program* sptr = prog->ptr; + int procNum = prog->procLookup[fo::ScriptProc::start]; + if (procNum != -1) { + fo::func::executeProcedure(sptr, procNum); + } + return procNum; +} + static void RunScript(GlobalScript* script) { script->count = 0; - RunScriptProc(&script->prog, fo::ScriptProc::start); // run "start" + if (script->startProc != -1) { + fo::func::executeProcedure(script->prog.ptr, script->startProc); // run "start" + } } /** @@ -599,8 +605,8 @@ static void RunGlobalScriptsOnWorldMap() { static DWORD _stdcall HandleMapUpdateForScripts(const DWORD procId) { if (procId == fo::ScriptProc::map_enter_p_proc) { // map changed, all game objects were destroyed and scripts detached, need to re-insert global scripts into the game - for (SfallProgsMap::iterator it = sfallProgsMap.begin(); it != sfallProgsMap.end(); it++) { - fo::func::runProgram(it->second.ptr); + for (std::vector::const_iterator it = globalScripts.cbegin(); it != globalScripts.cend(); it++) { + fo::func::runProgram(it->prog.ptr); } } else if (procId == fo::ScriptProc::map_exit_p_proc) onMapExit.invoke(); diff --git a/sfall/Modules/ScriptExtender.h b/sfall/Modules/ScriptExtender.h index c7473730..96bc8b0f 100644 --- a/sfall/Modules/ScriptExtender.h +++ b/sfall/Modules/ScriptExtender.h @@ -90,6 +90,8 @@ void RunScriptProc(ScriptProgram* prog, const char* procName); // execute script proc by procId from define.h void RunScriptProc(ScriptProgram* prog, long procId); +int RunScriptStartProc(ScriptProgram* prog); + void AddProgramToMap(ScriptProgram &prog); ScriptProgram* GetGlobalScriptProgram(fo::Program* scriptPtr); diff --git a/sfall/Modules/Scripting/Opcodes.cpp b/sfall/Modules/Scripting/Opcodes.cpp index dd3b6d73..d9490aa7 100644 --- a/sfall/Modules/Scripting/Opcodes.cpp +++ b/sfall/Modules/Scripting/Opcodes.cpp @@ -184,7 +184,7 @@ static SfallOpcodeInfo opcodeInfoArray[] = { {0x260, "reg_anim_turn_towards", sf_reg_anim_turn_towards, 3, false, {ARG_OBJECT, ARG_INT, ARG_INT}}, {0x261, "metarule2_explosions", sf_explosions_metarule, 3, true, {ARG_INT, ARG_INT, ARG_INT}}, - {0x262, "register_hook_proc", sf_register_hook, 2, false, {ARG_INT, ARG_ANY}}, + {0x262, "register_hook_proc", sf_register_hook, 2, false, {ARG_INT, ARG_INT}}, {0x263, "power", sf_power, 2, true, {ARG_NUMBER, ARG_NUMBER}}, {0x264, "log", sf_log, 1, true, {ARG_NUMBER}}, {0x265, "exponent", sf_exponent, 1, true, {ARG_NUMBER}}, diff --git a/sfall/Modules/Worldmap.cpp b/sfall/Modules/Worldmap.cpp index 16223792..788d4f96 100644 --- a/sfall/Modules/Worldmap.cpp +++ b/sfall/Modules/Worldmap.cpp @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -#include +#include #include #include "..\main.h" @@ -40,7 +40,7 @@ static DWORD ViewportY; struct levelRest { char level[4]; }; -std::map mapRestInfo; +std::unordered_map mapRestInfo; static bool restMap; static bool restMode; @@ -490,7 +490,7 @@ void PipBoyAutomapsPatch() { void Worldmap::SaveData(HANDLE file) { DWORD sizeWrite, count = mapRestInfo.size(); WriteFile(file, &count, 4, &sizeWrite, 0); - std::map::iterator it; + std::unordered_map::iterator it; for (it = mapRestInfo.begin(); it != mapRestInfo.end(); it++) { WriteFile(file, &it->first, 4, &sizeWrite, 0); WriteFile(file, &it->second, sizeof(levelRest), &sizeWrite, 0);