From cf90437b711227871df8567240bdf3b15c6b84cc Mon Sep 17 00:00:00 2001 From: NovaRain Date: Sun, 10 Jan 2021 21:45:50 +0800 Subject: [PATCH] Backported HOOK_GAMEMODECHANGE from 4.2 Backported SPECIAL game mode flag from 4.2 --- artifacts/scripting/headers/sfall.h | 2 + artifacts/scripting/hookscripts.txt | 9 + sfall/FalloutEngine.cpp | 3 + sfall/FalloutEngine.h | 3 + sfall/HookScripts.cpp | 24 ++- sfall/HookScripts.h | 2 + sfall/LoadGameHook.cpp | 249 +++++++++++++++++++--------- sfall/LoadGameHook.h | 2 +- 8 files changed, 209 insertions(+), 85 deletions(-) diff --git a/artifacts/scripting/headers/sfall.h b/artifacts/scripting/headers/sfall.h index 1009e99b..09af7215 100644 --- a/artifacts/scripting/headers/sfall.h +++ b/artifacts/scripting/headers/sfall.h @@ -22,6 +22,7 @@ #define HEROWIN (0x40000) #define DIALOGVIEW (0x80000) #define COUNTERWIN (0x100000) // counter window for moving multiple items or setting a timer +#define SPECIAL (0x80000000) //Valid arguments to register_hook #define HOOK_TOHIT (0) @@ -50,6 +51,7 @@ #define HOOK_WITHINPERCEPTION (23) #define HOOK_INVENTORYMOVE (24) #define HOOK_INVENWIELD (25) +#define HOOK_GAMEMODECHANGE (31) //Valid arguments to list_begin #define LIST_CRITTERS (0) diff --git a/artifacts/scripting/hookscripts.txt b/artifacts/scripting/hookscripts.txt index 69ddba7c..135da603 100644 --- a/artifacts/scripting/hookscripts.txt +++ b/artifacts/scripting/hookscripts.txt @@ -462,3 +462,12 @@ int arg3 - 1 when wielding, 0 when unwielding int arg4 - 1 when removing an equipped item from inventory, 0 otherwise int ret0 - overrides hard-coded handler (-1 - use engine handler, any other value - override) - NOT RECOMMENDED + +------------------------------------------- + +HOOK_GAMEMODECHANGE (hs_gamemodechange.int) + +Runs once every time when the game mode was changed, like opening/closing the inventory, character screen, pipboy, etc. + +int arg0 - event type: 1 - when the player exits the game, 0 - otherwise +int arg1 - the previous game mode diff --git a/sfall/FalloutEngine.cpp b/sfall/FalloutEngine.cpp index a2d786a3..0d47423a 100644 --- a/sfall/FalloutEngine.cpp +++ b/sfall/FalloutEngine.cpp @@ -434,6 +434,8 @@ const DWORD game_time_date_ = 0x4A3338; const DWORD gdDestroyHeadWindow_ = 0x447294; const DWORD gdialog_barter_cleanup_tables_ = 0x448660; const DWORD gdialog_barter_pressed_ = 0x44A52C; +const DWORD gdialog_window_create_ = 0x44A62C; +const DWORD gdialog_window_destroy_ = 0x44A9D8; const DWORD gdialogActive_ = 0x444D2C; const DWORD gdialogDisplayMsg_ = 0x445448; const DWORD gdialogFreeSpeech_ = 0x4450C4; @@ -573,6 +575,7 @@ const DWORD make_straight_path_ = 0x4163AC; const DWORD make_straight_path_func_ = 0x4163C8; const DWORD map_disable_bk_processes_ = 0x482104; const DWORD map_enable_bk_processes_ = 0x4820C0; +const DWORD map_exit_ = 0x482084; const DWORD map_get_short_name_ = 0x48261C; const DWORD map_load_idx_ = 0x482B34; const DWORD MapDirErase_ = 0x480040; diff --git a/sfall/FalloutEngine.h b/sfall/FalloutEngine.h index d35c23ba..cd0352f1 100644 --- a/sfall/FalloutEngine.h +++ b/sfall/FalloutEngine.h @@ -722,6 +722,8 @@ extern const DWORD game_time_date_; extern const DWORD gdDestroyHeadWindow_; extern const DWORD gdialog_barter_cleanup_tables_; extern const DWORD gdialog_barter_pressed_; +extern const DWORD gdialog_window_create_; +extern const DWORD gdialog_window_destroy_; extern const DWORD gdialogActive_; extern const DWORD gdialogDisplayMsg_; extern const DWORD gdialogFreeSpeech_; @@ -867,6 +869,7 @@ extern const DWORD make_straight_path_; extern const DWORD make_straight_path_func_; // (TGameObj *aObj, int aTileFrom, int a3, signed int aTileTo, TGameObj **aObjResult, int a5, int (*a6)(void)) extern const DWORD map_disable_bk_processes_; extern const DWORD map_enable_bk_processes_; +extern const DWORD map_exit_; extern const DWORD map_get_short_name_; extern const DWORD map_load_idx_; extern const DWORD MapDirErase_; diff --git a/sfall/HookScripts.cpp b/sfall/HookScripts.cpp index 74288980..6cd67319 100644 --- a/sfall/HookScripts.cpp +++ b/sfall/HookScripts.cpp @@ -1585,8 +1585,26 @@ long __stdcall CorrectFidForRemovedItem_wHook(TGameObj* critter, TGameObj* item, if (result) CorrectFidForRemovedItem(critter, item, flags); return result; } + +// 4.x backport +static unsigned long previousGameMode = 0; + +void __stdcall GameModeChangeHook(DWORD exit) { + BeginHook(); + argCount = 2; + args[0] = exit; + args[1] = previousGameMode; + RunHookScript(HOOK_GAMEMODECHANGE); + EndHook(); + + previousGameMode = GetLoopFlags(); +} // END HOOKS +static void HookCommon_Reset() { + previousGameMode = 0; +} + DWORD __stdcall GetHSArgCount() { return argCount; } @@ -1617,7 +1635,7 @@ void __stdcall SetHSReturn(DWORD value) { } void __stdcall RegisterHook(TProgram* script, int id, int procNum, bool specReg) { - if (id >= numHooks) return; + if (id >= numHooks || (id > HOOK_INVENWIELD && id < HOOK_GAMEMODECHANGE)) return; for (std::vector::iterator it = hooks[id].begin(); it != hooks[id].end(); ++it) { if (it->prog.ptr == script) { if (procNum == 0) hooks[id].erase(it); // unregister @@ -1855,6 +1873,8 @@ static void HookScriptInit() { HookCall(0x45C4F6, op_move_obj_inven_to_obj_hook); MakeCall(0x4778AF, item_drop_all_hack, 3); + LoadHookScript("hs_gamemodechange", HOOK_GAMEMODECHANGE); + DbFreeFileList(&filenames, 0); dlogr("Finished loading hook scripts.", DL_HOOK|DL_INIT); @@ -1865,6 +1885,7 @@ void HookScriptClear() { hooks[i].clear(); } std::memset(hooksInfo, 0, numHooks * sizeof(HooksPositionInfo)); + HookCommon_Reset(); } void LoadHookScripts() { @@ -1889,4 +1910,3 @@ void __stdcall RunHookScriptsAtProc(DWORD procId) { } } } - diff --git a/sfall/HookScripts.h b/sfall/HookScripts.h index 8f5b22d7..3057f3c4 100644 --- a/sfall/HookScripts.h +++ b/sfall/HookScripts.h @@ -46,6 +46,7 @@ enum HookType HOOK_WITHINPERCEPTION = 23, HOOK_INVENTORYMOVE = 24, HOOK_INVENWIELD = 25, + HOOK_GAMEMODECHANGE = 31, // 4.x backport HOOK_COUNT }; @@ -67,6 +68,7 @@ extern DWORD initingHookScripts; int __fastcall AmmoCostHook_Script(DWORD hookType, TGameObj* weapon, DWORD &rounds); void __stdcall RunHookScriptsAtProc(DWORD procId); +void __stdcall GameModeChangeHook(DWORD exit); void __stdcall KeyPressHook(DWORD* dxKey, bool pressed, DWORD vKey); void __stdcall MouseClickHook(DWORD button, bool pressed); diff --git a/sfall/LoadGameHook.cpp b/sfall/LoadGameHook.cpp index fcb0d2d6..99098817 100644 --- a/sfall/LoadGameHook.cpp +++ b/sfall/LoadGameHook.cpp @@ -30,10 +30,10 @@ #include "FileSystem.h" #include "Graphics.h" #include "HeroAppearance.h" +#include "HookScripts.h" #include "InputFuncs.h" #include "Interface.h" #include "Inventory.h" -#include "LoadGameHook.h" #include "LoadOrder.h" #include "Logging.h" #include "Message.h" @@ -50,6 +50,19 @@ #include "version.h" #include "Worldmap.h" +#include "LoadGameHook.h" + +#define _InLoop2(type, flag) __asm { \ + __asm push flag \ + __asm push type \ + __asm call SetInLoop \ +} +#define _InLoop(type, flag) __asm { \ + pushadc \ + _InLoop2(type, flag) \ + popadc \ +} + static DWORD inLoop = 0; static DWORD saveInCombatFix; static bool gameLoaded = false; @@ -87,6 +100,20 @@ void ClearLoopFlag(LoopFlag flag) { inLoop &= ~flag; } +static void __stdcall GameModeChange(DWORD state) { // OnGameModeChange + GameModeChangeHook(state); +} + +void __stdcall SetInLoop(DWORD mode, LoopFlag flag) { + unsigned long _inLoop = inLoop; + if (mode) { + SetLoopFlag(flag); + } else { + ClearLoopFlag(flag); + } + if (_inLoop ^ inLoop) GameModeChange(0); +} + static void __stdcall ResetState(DWORD onLoad) { // OnGameReset & OnBeforeGameStart BugFixes_OnGameLoad(); if (GraphicsMode > 3) Graphics_OnGameLoad(); @@ -189,10 +216,13 @@ static void __declspec(naked) SaveGame_hook() { test eax, eax; pop edx; // recall Mode parameter (pop eax) jz end; - mov eax, edx; - or inLoop, SAVEGAME; + push edx; + _InLoop2(1, SAVEGAME); + pop eax; call SaveGame_; - and inLoop, (-1 ^ SAVEGAME); + push eax; + _InLoop2(0, SAVEGAME); + pop eax; cmp eax, 1; jne end; call SaveGame2; // save sfall.sav @@ -262,9 +292,9 @@ errorLoad: static void __declspec(naked) LoadGame_hook() { __asm { - or inLoop, LOADGAME; + _InLoop(1, LOADGAME); call LoadGame_; - and inLoop, (-1 ^ LOADGAME); + _InLoop(0, LOADGAME); cmp eax, 1; jne end; // Invoked @@ -289,42 +319,6 @@ static void __declspec(naked) EndLoadHook() { } } -static void __stdcall GameInitialization() { // OnBeforeGameInit - BugFixes_Initialization(); - Interface_OnBeforeGameInit(); -} - -static void __stdcall game_init_hook() { // OnGameInit - FallbackEnglishLoadMsgFiles(); -} - -static void __stdcall GameInitialized(int initResult) { // OnAfterGameInit - #ifdef NDEBUG - if (!initResult) { - MessageBoxA(0, "Game initialization failed!", "Error", MB_TASKMODAL | MB_ICONERROR); - return; - } - #endif - RemoveSavFiles(); - Sound_OnAfterGameInit(); - BarBoxes_SetMaxSlots(); - if (Use32BitTalkingHeads) TalkingHeadsSetup(); -} - -static void __declspec(naked) main_init_system_hook() { - __asm { - pushadc; - call GameInitialization; - popadc; - call main_init_system_; - pushadc; - push eax; - call GameInitialized; - popadc; - retn; - } -} - static void NewGame2() { ResetState(0); @@ -348,6 +342,47 @@ static void __declspec(naked) NewGame() { } } +static void __stdcall GameInitialization() { // OnBeforeGameInit + BugFixes_Initialization(); + Interface_OnBeforeGameInit(); +} + +static void __stdcall game_init_hook() { // OnGameInit + FallbackEnglishLoadMsgFiles(); +} + +static void __stdcall GameInitialized(int initResult) { // OnAfterGameInit + #ifdef NDEBUG + if (!initResult) { + MessageBoxA(0, "Game initialization failed!", "Error", MB_TASKMODAL | MB_ICONERROR); + return; + } + #endif + RemoveSavFiles(); + Sound_OnAfterGameInit(); + BarBoxes_SetMaxSlots(); + if (Use32BitTalkingHeads) TalkingHeadsSetup(); +} + +static void __stdcall GameClose() { // OnBeforeGameClose + WipeSounds(); + ClearReadExtraGameMsgFiles(); +} + +static void __declspec(naked) main_init_system_hook() { + __asm { + pushadc; + call GameInitialization; + popadc; + call main_init_system_; + pushadc; + push eax; + call GameInitialized; + popadc; + retn; + } +} + static void __declspec(naked) MainMenuHook() { __asm { pushad; @@ -361,9 +396,14 @@ static void __declspec(naked) MainMenuHook() { } } -static void __stdcall GameClose() { // OnBeforeGameClose - WipeSounds(); - ClearReadExtraGameMsgFiles(); +static void __declspec(naked) before_game_exit_hook() { + __asm { + pushadc; + push 1; + call GameModeChange; + popadc; + jmp map_exit_; + } } static void __declspec(naked) game_close_hook() { @@ -381,7 +421,7 @@ static void __declspec(naked) WorldMapHook_Start() { test eax, eax; jl skip; push eax; - or inLoop, WORLDMAP; + _InLoop2(1, WORLDMAP); pop eax; skip: retn; @@ -391,7 +431,7 @@ skip: static void __declspec(naked) WorldMapHook_End() { __asm { push eax; - and inLoop, (-1 ^ WORLDMAP); + _InLoop2(0, WORLDMAP); pop eax; jmp remove_bk_process_; } @@ -401,12 +441,12 @@ static void __declspec(naked) CombatHook() { __asm { pushadc; call AICombatStart; - or inLoop, COMBAT; + _InLoop2(1, COMBAT); popadc; call combat_; pushadc; call AICombatEnd; - and inLoop, (-1 ^ COMBAT); + _InLoop2(0, COMBAT); popadc; retn; } @@ -414,45 +454,45 @@ static void __declspec(naked) CombatHook() { static void __declspec(naked) PlayerCombatHook() { __asm { - or inLoop, PCOMBAT; + _InLoop(1, PCOMBAT); call combat_input_; - and inLoop, (-1 ^ PCOMBAT); + _InLoop(0, PCOMBAT); retn; } } static void __declspec(naked) EscMenuHook() { __asm { - or inLoop, ESCMENU; + _InLoop(1, ESCMENU); call do_optionsFunc_; - and inLoop, (-1 ^ ESCMENU); + _InLoop(0, ESCMENU); retn; } } static void __declspec(naked) EscMenuHook2() { __asm { - or inLoop, ESCMENU; + _InLoop(1, ESCMENU); call do_options_; - and inLoop, (-1 ^ ESCMENU); + _InLoop(0, ESCMENU); retn; } } static void __declspec(naked) OptionsMenuHook() { __asm { - or inLoop, OPTIONS; + _InLoop(1, OPTIONS); call do_prefscreen_; - and inLoop, (-1 ^ OPTIONS); + _InLoop(0, OPTIONS); retn; } } static void __declspec(naked) HelpMenuHook() { __asm { - or inLoop, HELP; + _InLoop(1, HELP); call game_help_; - and inLoop, (-1 ^ HELP); + _InLoop(0, HELP); retn; } } @@ -460,7 +500,7 @@ static void __declspec(naked) HelpMenuHook() { static void __declspec(naked) CharacterHook() { __asm { push edx; - or inLoop, CHARSCREEN; + _InLoop2(1, CHARSCREEN); call PerksEnterCharScreen; xor eax, eax; call editor_design_; @@ -471,7 +511,7 @@ static void __declspec(naked) CharacterHook() { success: call PerksAcceptCharScreen; end: - and inLoop, (-1 ^ CHARSCREEN); + _InLoop2(0, CHARSCREEN); mov tagSkill4LevelBase, -1; // for fixing Tag! perk exploit pop edx; retn; @@ -480,7 +520,7 @@ end: static void __declspec(naked) DialogHook_Start() { __asm { - or inLoop, DIALOG; + _InLoop2(1, DIALOG); mov ebx, 1; retn; } @@ -488,15 +528,18 @@ static void __declspec(naked) DialogHook_Start() { static void __declspec(naked) DialogHook_End() { __asm { - and inLoop, (-1 ^ DIALOG); - jmp gdDestroyHeadWindow_; + and inLoop, ~DIALOG; // unset flag + _InLoop2(1, SPECIAL); // set the flag before animating the panel when exiting the dialog + call gdDestroyHeadWindow_; + _InLoop2(0, SPECIAL); + retn; } } static void __declspec(naked) PipboyHook_Start() { __asm { push eax; - or inLoop, PIPBOY; + _InLoop2(1, PIPBOY); pop eax; jmp win_draw_; } @@ -505,7 +548,7 @@ static void __declspec(naked) PipboyHook_Start() { static void __declspec(naked) PipboyHook_End() { __asm { push eax; - and inLoop, (-1 ^ PIPBOY); + _InLoop2(0, PIPBOY); pop eax; jmp win_delete_; } @@ -513,16 +556,16 @@ static void __declspec(naked) PipboyHook_End() { static void __declspec(naked) SkilldexHook() { __asm { - or inLoop, SKILLDEX; + _InLoop(1, SKILLDEX); call skilldex_select_; - and inLoop, (-1 ^ SKILLDEX); + _InLoop(0, SKILLDEX); retn; } } static void __declspec(naked) HandleInventoryHook_Start() { __asm { - or inLoop, INVENTORY; + _InLoop2(1, INVENTORY); xor eax, eax; jmp inven_set_mouse_; } @@ -530,7 +573,7 @@ static void __declspec(naked) HandleInventoryHook_Start() { static void __declspec(naked) HandleInventoryHook_End() { __asm { - and inLoop, (-1 ^ INVENTORY); + _InLoop2(0, INVENTORY); mov eax, esi; jmp exit_inventory_; } @@ -538,7 +581,7 @@ static void __declspec(naked) HandleInventoryHook_End() { static void __declspec(naked) UseInventoryOnHook_Start() { __asm { - or inLoop, INTFACEUSE; + _InLoop2(1, INTFACEUSE); xor eax, eax; jmp inven_set_mouse_; } @@ -546,7 +589,7 @@ static void __declspec(naked) UseInventoryOnHook_Start() { static void __declspec(naked) UseInventoryOnHook_End() { __asm { - and inLoop, (-1 ^ INTFACEUSE); + _InLoop2(0, INTFACEUSE); mov eax, edi; jmp exit_inventory_; } @@ -554,7 +597,7 @@ static void __declspec(naked) UseInventoryOnHook_End() { static void __declspec(naked) LootContainerHook_Start() { __asm { - or inLoop, INTFACELOOT; + _InLoop2(1, INTFACELOOT); xor eax, eax; jmp inven_set_mouse_; } @@ -564,7 +607,7 @@ static void __declspec(naked) LootContainerHook_End() { __asm { cmp dword ptr [esp + 0x150 - 0x58 + 4], 0; // JESSE_CONTAINER jz skip; // container is not created - and inLoop, (-1 ^ INTFACELOOT); + _InLoop2(0, INTFACELOOT); xor eax,eax; skip: call ResetBodyState; // reset object pointer used in calculating the weight/size of equipped and hidden items on NPCs after exiting loot/barter screens @@ -574,10 +617,11 @@ skip: static void __declspec(naked) BarterInventoryHook() { __asm { - or inLoop, BARTER; + and inLoop, ~SPECIAL; // unset flag after animating the dialog panel + _InLoop(1, BARTER); push [esp + 4]; call barter_inventory_; - and inLoop, (-1 ^ BARTER); + _InLoop(0, BARTER); call ResetBodyState; retn 4; } @@ -589,7 +633,7 @@ static void __declspec(naked) AutomapHook_Start() { test edx, edx; jnz skip; mov LoadGameHook_interfaceWID, ebp; - or inLoop, AUTOMAP; + _InLoop(1, AUTOMAP); skip: retn; } @@ -597,7 +641,7 @@ skip: static void __declspec(naked) AutomapHook_End() { __asm { - and inLoop, (-1 ^ AUTOMAP); + _InLoop(0, AUTOMAP); mov LoadGameHook_interfaceWID, -1 jmp win_delete_; } @@ -608,7 +652,9 @@ static void __declspec(naked) DialogReviewInitHook() { call gdReviewInit_; test eax, eax; jnz error; - or inLoop, DIALOGVIEW; + push ecx; + _InLoop2(1, DIALOGVIEW); + pop ecx; xor eax, eax; error: retn; @@ -617,25 +663,55 @@ error: static void __declspec(naked) DialogReviewExitHook() { __asm { - and inLoop, (-1 ^ DIALOGVIEW); + push ecx; + push eax; + _InLoop2(0, DIALOGVIEW); + pop eax; + pop ecx; jmp gdReviewExit_; } } static void __declspec(naked) setup_move_timer_win_Hook() { __asm { - or inLoop, COUNTERWIN; + _InLoop2(1, COUNTERWIN); jmp text_curr_; } } static void __declspec(naked) exit_move_timer_win_Hook() { __asm { - and inLoop, (-1 ^ COUNTERWIN); + push eax; + _InLoop2(0, COUNTERWIN); + pop eax; jmp win_delete_; } } +static void __declspec(naked) gdialog_bk_hook() { + __asm { + _InLoop2(1, SPECIAL); // set the flag before switching from dialog mode to barter + jmp gdialog_window_destroy_; + } +} + +static void __declspec(naked) gdialogUpdatePartyStatus_hook1() { + __asm { + push edx; + _InLoop2(1, SPECIAL); // set the flag before animating the dialog panel when a party member joins/leaves + pop edx; + jmp gdialog_window_destroy_; + } +} + +static void __declspec(naked) gdialogUpdatePartyStatus_hook0() { + __asm { + call gdialog_window_create_; + _InLoop2(0, SPECIAL); // unset the flag when entering the party member control panel + retn; + } +} + void LoadGameHook_Init() { saveInCombatFix = GetConfigInt("Misc", "SaveInCombatFix", 1); if (saveInCombatFix > 2) saveInCombatFix = 0; @@ -660,6 +736,7 @@ void LoadGameHook_Init() { HookCall(0x4426A6, game_init_hook); HookCall(0x480AB3, NewGame); + const DWORD loadSlotAddr[] = {0x47C72C, 0x47D1C9}; HookCalls(LoadSlot, loadSlotAddr); const DWORD loadGameAddr[] = {0x443AE4, 0x443B89, 0x480B77, 0x48FD35}; @@ -669,7 +746,10 @@ void LoadGameHook_Init() { HookCalls(SaveGame_hook, saveGameAddr); HookCall(0x480A28, MainMenuHook); + // 4.x backport + const DWORD beforeGameExitAddr[] = {0x480ACE, 0x480BC7}; // gnw_main_ + HookCalls(before_game_exit_hook, beforeGameExitAddr); HookCall(0x480CA7, game_close_hook); // gnw_main_ //HookCall(0x480D45, game_close_hook); // main_exit_system_ (never called) @@ -717,4 +797,9 @@ void LoadGameHook_Init() { HookCall(0x476AC6, setup_move_timer_win_Hook); // before init win HookCall(0x477067, exit_move_timer_win_Hook); + + // Set and unset the Special flag of game mode when animating the dialog interface panel + HookCall(0x447A7E, gdialog_bk_hook); // set when switching from dialog mode to barter mode (unset when entering barter) + HookCall(0x4457B1, gdialogUpdatePartyStatus_hook1); // set when a party member joins/leaves + HookCall(0x4457BC, gdialogUpdatePartyStatus_hook0); // unset } diff --git a/sfall/LoadGameHook.h b/sfall/LoadGameHook.h index 2537d8f1..ead98461 100644 --- a/sfall/LoadGameHook.h +++ b/sfall/LoadGameHook.h @@ -54,7 +54,7 @@ enum LoopFlag : unsigned long { DIALOGVIEW = 1 << 19, // 0x80000 COUNTERWIN = 1 << 20, // 0x100000 Counter window for moving multiple items or setting a timer -// SPECIAL = 1UL << 31 // 0x80000000 Additional special flag for all modes + SPECIAL = 1UL << 31 // 0x80000000 Additional special flag for all modes }; DWORD GetLoopFlags();