diff --git a/artifacts/scripting/hookscripts.txt b/artifacts/scripting/hookscripts.txt index 1da5b112..551a816e 100644 --- a/artifacts/scripting/hookscripts.txt +++ b/artifacts/scripting/hookscripts.txt @@ -46,6 +46,9 @@ Used from a normal global script if you want to run it at the same point a full > 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" 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!). +> void register_hook_proc_spec(int hook, procedure proc) +Works very similar to register_hook_proc, except that it registers the current script at the end of the hook script execution chain (i.e. the script will be executed after all previously registered scripts for the same hook, including the hs_*.int script.) All scripts hooked to a single hook point with this function are executed in exact order of how they were registered, as opposed to the description below, which refers to using register_hook/register_hook_proc functions. + 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: diff --git a/artifacts/scripting/sfall function notes.txt b/artifacts/scripting/sfall function notes.txt index 6bb2e18b..707f3c29 100644 --- a/artifacts/scripting/sfall function notes.txt +++ b/artifacts/scripting/sfall function notes.txt @@ -316,6 +316,10 @@ Some utility/math functions are available: - second argument should be passed just like you pass procedures to functions like gsay_option, giq_option, etc (name without quotes) - see "hookscripts.txt" for more details +> void register_hook_proc_spec(int hook, procedure proc) +- works just like "register_hook_proc", but allows to register a script at the end of the hook script execution chain (i.e. the script will be executed after all previously registered scripts for the same hook, including the hs_*.int script) +- to unregister hook script from current global script, use the register_hook_proc function + > string message_str_game(int fileId, int messageId) - works exactly the same as message_str, except you get messages from files in "text\\game\" directory - use GAME_MSG_* defines or mstr_* macros from sfall.h to use specific msg file diff --git a/artifacts/scripting/sfall opcode list.txt b/artifacts/scripting/sfall opcode list.txt index 4fa5563a..79f0026f 100644 --- a/artifacts/scripting/sfall opcode list.txt +++ b/artifacts/scripting/sfall opcode list.txt @@ -357,6 +357,8 @@ 0x827b - any sfall_func5(string funcName, arg1, arg2, arg3, arg4, arg5) 0x827c - any sfall_func6(string funcName, arg1, arg2, arg3, arg4, arg5, arg6) +0x827d - void register_hook_proc_spec(int hook, procedure proc) + * These functions require AllowUnsafeScripting to be enabled in ddraw.ini diff --git a/sfall/FalloutEngine/EngineUtils.cpp b/sfall/FalloutEngine/EngineUtils.cpp index a3904f18..dea4340f 100644 --- a/sfall/FalloutEngine/EngineUtils.cpp +++ b/sfall/FalloutEngine/EngineUtils.cpp @@ -181,14 +181,6 @@ long GetScriptLocalVars(long sid) { return (script) ? script->numLocalVars : 0; } -fo::GameObject* __fastcall LineOfSight(fo::GameObject* obj) { - long objTile = obj->tile; - fo::GameObject* object = fo::func::obj_blocking_at_wrapper(obj, objTile, obj->elevation, (void*)fo::funcoffs::obj_sight_blocking_at_); - if (object) objTile = fo::func::tile_num_in_direction(objTile, fo::func::tile_dir(objTile, fo::var::obj_dude->tile), 1); - fo::func::make_straight_path_func(fo::var::obj_dude, fo::var::obj_dude->tile, objTile, 0, (DWORD*)&object, 4, (void*)fo::funcoffs::obj_sight_blocking_at_); - return object; -} - //--------------------------------------------------------- //print text to surface void PrintText(char *DisplayText, BYTE ColourIndex, DWORD Xpos, DWORD Ypos, DWORD TxtWidth, DWORD ToWidth, BYTE *ToSurface) { diff --git a/sfall/FalloutEngine/EngineUtils.h b/sfall/FalloutEngine/EngineUtils.h index 35835cc1..8832ac18 100644 --- a/sfall/FalloutEngine/EngineUtils.h +++ b/sfall/FalloutEngine/EngineUtils.h @@ -76,8 +76,6 @@ bool IsPartyMember(fo::GameObject* critter); // Returns the number of local variables of the object script long GetScriptLocalVars(long sid); -fo::GameObject* __fastcall LineOfSight(fo::GameObject* obj); - // Print text to surface void PrintText(char *displayText, BYTE colorIndex, DWORD x, DWORD y, DWORD textWidth, DWORD destWidth, BYTE *surface); // gets the height of the currently selected font diff --git a/sfall/Modules/HookScripts.cpp b/sfall/Modules/HookScripts.cpp index 84db08dc..2b9b09bc 100644 --- a/sfall/Modules/HookScripts.cpp +++ b/sfall/Modules/HookScripts.cpp @@ -43,6 +43,12 @@ struct HooksInjectInfo { bool isInject; }; +static struct HooksPositionInfo { + long hsPosition = 0; // index of the hs_* script, or the beginning of the position for registering scripts using register_hook +// long positionShift = 0; // offset to the last script registered by register_hook + bool hasHsScript = false; +} hooksInfo[HOOK_COUNT]; + static HooksInjectInfo injectHooks[] = { {HOOK_TOHIT, Inject_ToHitHook, false}, {HOOK_AFTERHITROLL, Inject_AfterHitRollHook, false}, @@ -167,7 +173,7 @@ bool HookScripts::HookHasScript(int hookId) { return (hooks[hookId].empty() == false); } -void _stdcall RegisterHook(fo::Program* script, int id, int procNum) { +void RegisterHook(fo::Program* script, int id, int procNum, bool specReg) { if (id >= numHooks) return; for (std::vector::iterator it = hooks[id].begin(); it != hooks[id].end(); ++it) { if (it->prog.ptr == script) { @@ -184,7 +190,14 @@ void _stdcall RegisterHook(fo::Program* script, int id, int procNum) { hook.prog = *prog; hook.callback = procNum; hook.isGlobalScript = true; - hooks[id].push_back(hook); + + auto c_it = hooks[id].cend(); + if (specReg) { + c_it = hooks[id].cbegin(); + hooksInfo[id].hsPosition++; + } + hooks[id].insert(c_it, hook); + switch (id) { case HOOK_KEYPRESS: case HOOK_MOUSECLICK: @@ -218,6 +231,7 @@ void HookScriptClear() { for(int i = 0; i < numHooks; i++) { hooks[i].clear(); } + memset(hooksInfo, 0, HOOK_COUNT * sizeof(HooksPositionInfo)); } void LoadHookScripts() { @@ -226,6 +240,7 @@ void LoadHookScripts() { initingHookScripts = 1; for (int i = 0; i < numHooks; i++) { if (!hooks[i].empty()) { + hooksInfo[i].hasHsScript = true; InitScriptProgram(hooks[i][0].prog); // zero hook is always hs_*.int script because Hook scripts are loaded BEFORE global scripts } } @@ -234,10 +249,10 @@ void LoadHookScripts() { } // run specific event procedure for all hook scripts -void _stdcall RunHookScriptsAtProc(DWORD procId) { +void RunHookScriptsAtProc(DWORD procId) { for (int i = 0; i < numHooks; i++) { - if (!hooks[i].empty() && !hooks[i][0].isGlobalScript) { - RunScriptProc(&hooks[i][0].prog, procId); // run hs_*.int + if (hooksInfo[i].hasHsScript /*&& !hooks[i][hooksInfo[i].hsPosition].isGlobalScript*/) { + RunScriptProc(&hooks[i][hooksInfo[i].hsPosition].prog, procId); // run hs_*.int } } } diff --git a/sfall/Modules/HookScripts.h b/sfall/Modules/HookScripts.h index 9836c37d..0a81b86b 100644 --- a/sfall/Modules/HookScripts.h +++ b/sfall/Modules/HookScripts.h @@ -93,7 +93,7 @@ void _stdcall SetHSArg(DWORD id, DWORD value); void _stdcall SetHSReturn(DWORD d); // register hook by proc num (special values: -1 - use default (start) procedure, 0 - unregister) -void _stdcall RegisterHook(fo::Program* script, int id, int procNum); +void RegisterHook(fo::Program* script, int id, int procNum, bool specReg); // TODO: move void HookScriptClear(); @@ -101,6 +101,6 @@ void LoadHookScripts(); extern DWORD initingHookScripts; extern int __fastcall AmmoCostHook_Script(DWORD hookType, fo::GameObject* weapon, DWORD &rounds); -void _stdcall RunHookScriptsAtProc(DWORD procId); +void RunHookScriptsAtProc(DWORD procId); } diff --git a/sfall/Modules/ScriptExtender.cpp b/sfall/Modules/ScriptExtender.cpp index ee1b76b0..65c61786 100644 --- a/sfall/Modules/ScriptExtender.cpp +++ b/sfall/Modules/ScriptExtender.cpp @@ -617,7 +617,7 @@ static DWORD _stdcall HandleMapUpdateForScripts(const DWORD procId) { } // run all global scripts of types 0 and 3 at specific procedure (if exist) -void _stdcall RunGlobalScriptsAtProc(DWORD procId) { +void RunGlobalScriptsAtProc(DWORD procId) { for (DWORD d = 0; d < globalScripts.size(); d++) { if (globalScripts[d].mode != 0 && globalScripts[d].mode != 3) continue; RunScriptProc(&globalScripts[d].prog, procId); diff --git a/sfall/Modules/ScriptExtender.h b/sfall/Modules/ScriptExtender.h index 96bc8b0f..c491b08c 100644 --- a/sfall/Modules/ScriptExtender.h +++ b/sfall/Modules/ScriptExtender.h @@ -56,7 +56,7 @@ void __fastcall SetGlobalScriptRepeat(fo::Program* script, int frames); void __fastcall SetGlobalScriptType(fo::Program* script, int type); bool _stdcall IsGameScript(const char* filename); -void _stdcall RunGlobalScriptsAtProc(DWORD procId); +void RunGlobalScriptsAtProc(DWORD procId); bool LoadGlobals(HANDLE h); void SaveGlobals(HANDLE h); diff --git a/sfall/Modules/Scripting/Handlers/Core.cpp b/sfall/Modules/Scripting/Handlers/Core.cpp index 671e7c82..b086ab59 100644 --- a/sfall/Modules/Scripting/Handlers/Core.cpp +++ b/sfall/Modules/Scripting/Handlers/Core.cpp @@ -221,12 +221,19 @@ end: // used for both register_hook and register_hook_proc void sf_register_hook(OpcodeContext& ctx) { - int id = ctx.arg(0).asInt(); - int proc = (ctx.numArgs() > 1) - ? ctx.arg(1).asInt() - : -1; - - RegisterHook(ctx.program(), id, proc); + bool specReg = false; + int proc; + switch (ctx.opcode()) { + case 0x27d: + specReg = true; + case 0x262: + proc = ctx.arg(1).rawValue(); + if (proc < 0 || (specReg && proc == 0)) return; + break; + default: + proc = -1; + } + RegisterHook(ctx.program(), ctx.arg(0).rawValue(), proc, specReg); } void sf_sfall_ver_major(OpcodeContext& ctx) { diff --git a/sfall/Modules/Scripting/Opcodes.cpp b/sfall/Modules/Scripting/Opcodes.cpp index d9490aa7..b2da066c 100644 --- a/sfall/Modules/Scripting/Opcodes.cpp +++ b/sfall/Modules/Scripting/Opcodes.cpp @@ -210,6 +210,8 @@ static SfallOpcodeInfo opcodeInfoArray[] = { {0x27a, "sfall_func4", HandleMetarule, 5, true}, {0x27b, "sfall_func5", HandleMetarule, 6, true}, {0x27c, "sfall_func6", HandleMetarule, 7, true}, // if you need more arguments - use arrays + + {0x27d, "register_hook_proc_spec", sf_register_hook, 2, false, {ARG_INT, ARG_INT}}, }; // A hash-table for opcode info, indexed by opcode.