From 8d813a952e3432b610d17ea2b76df99d088b0ea0 Mon Sep 17 00:00:00 2001 From: NovaRain Date: Fri, 14 Aug 2020 21:31:06 +0800 Subject: [PATCH] Fixed and improved the behavior of nested timer events in global scripts Added a fix to prevent the execution of critter_p_proc and game events when playing movies. Added a debug message about a corrupted proto file. Some minor code/document edits. --- artifacts/scripting/headers/sfall.h | 2 +- sfall/BugFixes.cpp | 14 ++++++++ sfall/DebugEditor.cpp | 19 +++++++++- sfall/ExtraSaveSlots.cpp | 2 +- sfall/FalloutEngine.cpp | 1 + sfall/FalloutEngine.h | 1 + sfall/Graphics.cpp | 6 ++-- sfall/ScriptExtender.cpp | 56 +++++++++++++++++++++++------ 8 files changed, 83 insertions(+), 18 deletions(-) diff --git a/artifacts/scripting/headers/sfall.h b/artifacts/scripting/headers/sfall.h index 3da15ce6..4be34668 100644 --- a/artifacts/scripting/headers/sfall.h +++ b/artifacts/scripting/headers/sfall.h @@ -253,7 +253,7 @@ /* sfall metarule3 function macros */ // sets the number of days (range 1...127) for the Frank Horrigan encounter, or disable the encounter if days is set to 0 #define set_horrigan_days(day) metarule3(200, day, 0, 0) -// clears the keyboard input buffer, use it in the HOOK_KEYPRESS hook to clear keyboard events before calling script functions that accept key input +// clears the keyboard input buffer, use it in the HOOK_KEYPRESS hook to clear keyboard events before calling functions that are waiting for keyboard input #define clear_keyboard_buffer metarule3(201, 0, 0, 0) diff --git a/sfall/BugFixes.cpp b/sfall/BugFixes.cpp index 6bfb5163..338c4bcd 100644 --- a/sfall/BugFixes.cpp +++ b/sfall/BugFixes.cpp @@ -2749,6 +2749,17 @@ checkTiles: } } +static void __declspec(naked) doBkProcesses_hook() { + __asm { + call gdialogActive_; + test eax, eax; + jz skip; + retn; +skip: + jmp gmovieIsPlaying_; + } +} + void BugFixesInit() { #ifndef NDEBUG @@ -3480,4 +3491,7 @@ void BugFixesInit() }; SafeWriteBytes(0x42A0F4, codeData1, 18); // ai_move_steps_closer_ HookCall(0x42A0F8, (void*)obj_dist_); + + // Fix to prevent the execution of critter_p_proc and game events when playing movies (same as when the dialog is active) + HookCall(0x4A3C89, doBkProcesses_hook); } diff --git a/sfall/DebugEditor.cpp b/sfall/DebugEditor.cpp index 944aaf82..770fa5ff 100644 --- a/sfall/DebugEditor.cpp +++ b/sfall/DebugEditor.cpp @@ -335,6 +335,20 @@ display: } } +static void __declspec(naked) proto_load_pid_hack() { + static char* proDbgMsg = "\nERROR reading prototype file: %s\n"; + __asm { + mov dword ptr [esp + 0x120 - 0x1C + 4], -1; + lea eax, [esp + 0x120 - 0x120 + 4]; // pro file + push eax; + push proDbgMsg; + call debug_printf_; + add esp, 8; + mov eax, 0x500494; // 'iisxxxx1' + jmp gsound_play_sfx_file_; + } +} + static void __declspec(naked) win_debug_hook() { __asm { call debug_log_; @@ -381,7 +395,7 @@ static void DebugModePatch() { if (iniGetInt("Debugging", "HideObjIsNullMsg", 0, ddrawIniDef)) { MakeJump(0x453FD2, dbg_error_hack); } - // prints a debug message about missing art file for critters to both debug.log and the message window in sfall debugging mode + // prints a debug message about a missing critter art file to both debug.log and the message window in sfall debugging mode HookCall(0x419B65, art_data_size_hook); // Fix to prevent crashes when there is a '%' character in the printed message @@ -408,6 +422,9 @@ static void DontDeleteProtosPatch() { void DebugEditorInit() { DebugModePatch(); + // Notifies and prints a debug message about a corrupted proto file to debug.log + MakeCall(0x4A1D73, proto_load_pid_hack, 6); + if (!isDebug) return; DontDeleteProtosPatch(); diff --git a/sfall/ExtraSaveSlots.cpp b/sfall/ExtraSaveSlots.cpp index 8aadd02f..ffe2a526 100644 --- a/sfall/ExtraSaveSlots.cpp +++ b/sfall/ExtraSaveSlots.cpp @@ -534,7 +534,7 @@ void ExtraSaveSlotsInit() { dlogr(" Done", DL_INIT); } - // Adds the city name in the description of a save slot + // Adds the city name in the description for empty save slots MakeJump(0x47F02C, GetComment_hack); } diff --git a/sfall/FalloutEngine.cpp b/sfall/FalloutEngine.cpp index 8a9f5db1..e316d6b6 100644 --- a/sfall/FalloutEngine.cpp +++ b/sfall/FalloutEngine.cpp @@ -433,6 +433,7 @@ const DWORD gmouse_3d_set_mode_ = 0x44CA18; const DWORD gmouse_is_scrolling_ = 0x44B54C; const DWORD gmouse_set_cursor_ = 0x44C840; const DWORD gmovie_play_ = 0x44E690; +const DWORD gmovieIsPlaying_ = 0x44EB14; const DWORD GNW_do_bk_process_ = 0x4C8D1C; const DWORD GNW_find_ = 0x4D7888; const DWORD GNW_win_refresh_ = 0x4D6FD8; diff --git a/sfall/FalloutEngine.h b/sfall/FalloutEngine.h index 1f9fb33c..adc890c6 100644 --- a/sfall/FalloutEngine.h +++ b/sfall/FalloutEngine.h @@ -712,6 +712,7 @@ extern const DWORD gmouse_3d_set_mode_; extern const DWORD gmouse_is_scrolling_; extern const DWORD gmouse_set_cursor_; extern const DWORD gmovie_play_; +extern const DWORD gmovieIsPlaying_; extern const DWORD GNW_do_bk_process_; extern const DWORD GNW_find_; extern const DWORD GNW_win_refresh_; diff --git a/sfall/Graphics.cpp b/sfall/Graphics.cpp index f9d141c9..a1a545f4 100644 --- a/sfall/Graphics.cpp +++ b/sfall/Graphics.cpp @@ -460,8 +460,7 @@ static void Present() { DWORD move = r.right - r2.right; windowLeft -= move; r.right -= move; - } - else if (r.left < r2.left) { + } else if (r.left < r2.left) { DWORD move = r2.left - r.left; windowLeft += move; r.right += move; @@ -470,8 +469,7 @@ static void Present() { DWORD move = r.bottom - r2.bottom; windowTop -= move; r.bottom -= move; - } - else if (r.top < r2.top) { + } else if (r.top < r2.top) { DWORD move = r2.top - r.top; windowTop += move; r.bottom += move; diff --git a/sfall/ScriptExtender.cpp b/sfall/ScriptExtender.cpp index b8cae85b..ed3b4995 100644 --- a/sfall/ScriptExtender.cpp +++ b/sfall/ScriptExtender.cpp @@ -18,6 +18,7 @@ //#include #include +#include #include "main.h" #include "FalloutEngine.h" @@ -398,16 +399,20 @@ struct SelfOverrideObj { } }; +// Events in global scripts cannot be saved because the scripts don't have a binding object struct TimedEvent { sScriptProgram* script; unsigned long time; long fixed_param; + bool isActive; bool operator() (const TimedEvent &a, const TimedEvent &b) { return a.time < b.time; } } *timedEvent = nullptr; +static long executeTimedEventDepth = 0; +static std::stack executeTimedEvents; static std::list timerEventScripts; static std::vector checkedScripts; @@ -1372,6 +1377,9 @@ static void ClearGlobalScripts() { selfOverrideMap.clear(); globalExportedVars.clear(); timerEventScripts.clear(); + timedEvent = nullptr; + executeTimedEventDepth = 0; + while (!executeTimedEvents.empty()) executeTimedEvents.pop(); HookScriptClear(); } @@ -1523,25 +1531,50 @@ static DWORD __stdcall HandleMapUpdateForScripts(const DWORD procId) { static DWORD HandleTimedEventScripts() { DWORD currentTime = *ptr_fallout_game_time; - bool wasRunning = false; - std::list::const_iterator timerIt = timerEventScripts.cbegin(); - for (; timerIt != timerEventScripts.cend(); ++timerIt) { + if (timerEventScripts.empty()) return currentTime; + + executeTimedEventDepth++; + + DevPrintf("\n[TimedEventScripts] Time: %d / Depth: %d", currentTime, executeTimedEventDepth); + + bool eventsWereRunning = false; + for (std::list::const_iterator timerIt = timerEventScripts.cbegin(); timerIt != timerEventScripts.cend(); ++timerIt) { + DevPrintf("\n[TimedEventScripts] Event: %d", timerIt->time); + if (timerIt->isActive == false) continue; if (currentTime >= timerIt->time) { + if (timedEvent) executeTimedEvents.push(timedEvent); // store a pointer to the currently running event + timedEvent = const_cast(&(*timerIt)); - DevPrintf("\n[TimedEventScripts] run event: %d", timerIt->time); + timedEvent->isActive = false; + + DevPrintf("\n[TimedEventScripts] Run event: %d", timerIt->time); RunScriptProc(timerIt->script, Scripts::timed_event_p_proc); - wasRunning = true; + DevPrintf("\n[TimedEventScripts] Event done: %d", timerIt->time); + + timedEvent = nullptr; + if (!executeTimedEvents.empty()) { + timedEvent = executeTimedEvents.top(); // restore a pointer to a previously running event + executeTimedEvents.pop(); + } + eventsWereRunning = true; } else { break; } } - if (wasRunning) { - for (std::list::const_iterator _it = timerEventScripts.cbegin(); _it != timerIt; ++_it) { - DevPrintf("\n[TimedEventScripts] delete event: %d", _it->time); + executeTimedEventDepth--; + + if (eventsWereRunning && executeTimedEventDepth == 0) { + timedEvent = nullptr; + // delete all previously executed events + for (std::list::const_iterator it = timerEventScripts.cbegin(); it != timerEventScripts.cend();) { + if (it->isActive == false) { + DevPrintf("\n[TimedEventScripts] Remove event: %d", it->time); + it = timerEventScripts.erase(it); + } else { + ++it; + } } - timerEventScripts.erase(timerEventScripts.cbegin(), timerIt); } - timedEvent = nullptr; return currentTime; } @@ -1553,7 +1586,7 @@ static DWORD TimedEventNextTime() { mov nextTime, eax; push edx; } - if (!timerEventScripts.empty()) { + if (!timerEventScripts.empty() && timerEventScripts.front().isActive) { DWORD time = timerEventScripts.front().time; if (!nextTime || time < nextTime) nextTime = time; } @@ -1569,6 +1602,7 @@ static DWORD script_chk_timed_events_hook() { void __stdcall AddTimerEventScripts(DWORD script, long time, long param) { sScriptProgram* scriptProg = &(sfallProgsMap.find(script)->second); TimedEvent timer; + timer.isActive = true; timer.script = scriptProg; timer.fixed_param = param; timer.time = *ptr_fallout_game_time + time;