diff --git a/artifacts/ddraw.ini b/artifacts/ddraw.ini index c029eef3..192b2970 100644 --- a/artifacts/ddraw.ini +++ b/artifacts/ddraw.ini @@ -792,6 +792,9 @@ HideObjIsNullMsg=0 ;Has pretty nasty side effects when saving/reloading, so don't use for regular gameplay DontDeleteProtos=0 +;Set to 1 to force sfall to search for global scripts every time the game loads rather than only the first time +AlwaysFindScripts=0 + ;Set to 1 to force critters to display combat float messages ;Requires AllowSoundForFloats to be enabled Test_ForceFloats=0 diff --git a/sfall/BugFixes.cpp b/sfall/BugFixes.cpp index 0fb69dfe..d061e8e1 100644 --- a/sfall/BugFixes.cpp +++ b/sfall/BugFixes.cpp @@ -2778,7 +2778,6 @@ isDeath: static void __declspec(naked) obj_move_to_tile_hack_ondeath() { static const DWORD obj_move_to_tile_Ret = 0x48A759; __asm { - test esi, esi; jz skip; cmp dudeIsAnimDeath, 0; jnz skip; @@ -2789,6 +2788,16 @@ skip: } } +static void __declspec(naked) action_knockback_hack() { + __asm { + mov ecx, 20; // cap knockback distance + cmp ebp, ecx; + cmovg ebp, ecx; + mov ecx, 1; + retn; + } +} + void BugFixes_OnGameLoad() { dudeIsAnimDeath = false; } @@ -3532,4 +3541,7 @@ void BugFixesInit() // (e.g. fire dance or knockback animation) MakeCall(0x41094B, show_damage_to_object_hack, 1); MakeCall(0x48A6CB, obj_move_to_tile_hack_ondeath, 1); + + // Fix to limit the maximum distance for knockback animation + MakeCall(0x4104D5, action_knockback_hack); } diff --git a/sfall/HookScripts.cpp b/sfall/HookScripts.cpp index ba96a249..d11a5b02 100644 --- a/sfall/HookScripts.cpp +++ b/sfall/HookScripts.cpp @@ -1613,7 +1613,7 @@ static void LoadHookScript(const char* name, int id) { } } -static void HookScriptInit2() { +static void HookScriptInit() { dlogr("Loading hook scripts:", DL_HOOK|DL_INIT); char* mask = "scripts\\hs_*.int"; @@ -1812,9 +1812,9 @@ void HookScriptClear() { std::memset(hooksInfo, 0, HOOK_COUNT * sizeof(HooksPositionInfo)); } -void HookScriptInit() { +void LoadHookScripts() { isGlobalScriptLoading = 1; // this should allow to register global exported variables - HookScriptInit2(); + HookScriptInit(); initingHookScripts = 1; for (int i = 0; i < numHooks; i++) { if (!hooks[i].empty()) { diff --git a/sfall/HookScripts.h b/sfall/HookScripts.h index 35c46c75..14203596 100644 --- a/sfall/HookScripts.h +++ b/sfall/HookScripts.h @@ -51,16 +51,16 @@ enum HookType DWORD __stdcall GetHSArgCount(); DWORD __stdcall GetHSArg(); -DWORD* __stdcall GetHSArgs(); DWORD __stdcall GetHSArgAt(DWORD id); +DWORD* __stdcall GetHSArgs(); 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(TProgram* script, int id, int procNum, bool specReg); -void HookScriptInit(); void HookScriptClear(); +void LoadHookScripts(); extern DWORD initingHookScripts; int __fastcall AmmoCostHook_Script(DWORD hookType, TGameObj* weapon, DWORD &rounds); diff --git a/sfall/ScriptExtender.cpp b/sfall/ScriptExtender.cpp index b62d762b..72864e0a 100644 --- a/sfall/ScriptExtender.cpp +++ b/sfall/ScriptExtender.cpp @@ -415,6 +415,8 @@ static long executeTimedEventDepth = 0; static std::stack executeTimedEvents; static std::list timerEventScripts; +static std::vector globalScriptFilesList; + static std::vector checkedScripts; static std::vector globalScripts; @@ -437,6 +439,7 @@ static void* opcodes[0x300]; DWORD availableGlobalScriptTypes = 0; DWORD isGlobalScriptLoading = 0; bool isGameLoading; +bool alwaysFindScripts; bool displayWinUpdateState = false; TScript overrideScriptStruct = {0}; @@ -1294,18 +1297,36 @@ bool __stdcall IsGameScript(const char* filename) { return false; } -// this runs after the game was loaded/started -void LoadGlobalScripts() { - isGameLoading = false; - HookScriptInit(); - dlogr("Loading global scripts:", DL_SCRIPT|DL_INIT); +static void LoadGlobalScriptsList() { + sScriptProgram prog; + for (std::vector::const_iterator it = globalScriptFilesList.begin(); it != globalScriptFilesList.end(); ++it) { + const std::string &scriptFile = *it; + dlog("> ", DL_SCRIPT); + dlog(scriptFile.c_str(), DL_SCRIPT); + isGlobalScriptLoading = 1; + LoadScriptProgram(prog, scriptFile.c_str()); + if (prog.ptr) { + dlogr(" Done", DL_SCRIPT); + sGlobalScript gscript = sGlobalScript(prog); + gscript.startProc = prog.procLookup[Scripts::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 + InitScriptProgram(prog); + } else { + dlogr(" Error!", DL_SCRIPT); + } + isGlobalScriptLoading = 0; + } +} + +static void PrepareGlobalScriptsList() { + globalScriptFilesList.clear(); char* name = "scripts\\gl*.int"; char** filenames; int count = DbGetFileList(name, &filenames); - // TODO: refactor script programs - sScriptProgram prog; for (int i = 0; i < count; i++) { name = _strlwr(filenames[i]); // name of the script in lower case if (name[0] != 'g' || name[1] != 'l') continue; // fix bug in db_get_file_list fuction (if the script name begins with a non-Latin character) @@ -1316,26 +1337,25 @@ void LoadGlobalScripts() { baseName = baseName.substr(0, lastDot); // script name without extension if (!IsGameScript(baseName.c_str())) { - dlog("> ", DL_SCRIPT); - dlog(name, DL_SCRIPT); - isGlobalScriptLoading = 1; - LoadScriptProgram(prog, baseName.c_str()); - if (prog.ptr) { - dlogr(" Done", DL_SCRIPT); - sGlobalScript gscript = sGlobalScript(prog); - gscript.startProc = prog.procLookup[Scripts::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 - InitScriptProgram(prog); - } else { - dlogr(" Error!", DL_SCRIPT); - } - isGlobalScriptLoading = 0; + globalScriptFilesList.push_back(baseName); } } DbFreeFileList(&filenames, 0); +} +// this runs after the game was loaded/started +void LoadGlobalScripts() { + static bool listIsPrepared = false; + isGameLoading = false; + + LoadHookScripts(); + + dlogr("Loading global scripts:", DL_SCRIPT|DL_INIT); + if (!listIsPrepared) { // only once + PrepareGlobalScriptsList(); + listIsPrepared = !alwaysFindScripts; + } + LoadGlobalScriptsList(); dlogr("Finished loading global scripts.", DL_SCRIPT|DL_INIT); } @@ -1509,7 +1529,7 @@ static DWORD HandleTimedEventScripts() { DevPrintf("\n[TimedEventScripts] Event: %d", it->time); } - bool eventsWereRunning = false; + bool eventWasRunning = false; for (std::list::const_iterator timerIt = timerEventScripts.cbegin(); timerIt != timerEventScripts.cend(); ++timerIt) { if (timerIt->isActive == false) continue; if (currentTime >= timerIt->time) { @@ -1527,14 +1547,14 @@ static DWORD HandleTimedEventScripts() { timedEvent = executeTimedEvents.top(); // restore a pointer to a previously running event executeTimedEvents.pop(); } - eventsWereRunning = true; + eventWasRunning = true; } else { break; } } executeTimedEventDepth--; - if (eventsWereRunning && executeTimedEventDepth == 0) { + if (eventWasRunning && executeTimedEventDepth == 0) { timedEvent = nullptr; // delete all previously executed events for (std::list::const_iterator it = timerEventScripts.cbegin(); it != timerEventScripts.cend();) { @@ -1719,6 +1739,9 @@ void ScriptExtenderInit() { dlogr("Arrays in backward-compatiblity mode.", DL_SCRIPT); } + alwaysFindScripts = isDebug && (iniGetInt("Debugging", "AlwaysFindScripts", 0, ddrawIniDef) != 0); + if (alwaysFindScripts) dlogr("Always searching for global scripts behavior enabled.", DL_SCRIPT); + HookCall(0x480E7B, MainGameLoopHook); // hook the main game loop HookCall(0x422845, CombatLoopHook); // hook the combat loop MakeCall(0x4230D5, AfterCombatAttackHook);