diff --git a/src/game/Game.cpp b/src/game/Game.cpp index f41bc62..d8eab6e 100644 --- a/src/game/Game.cpp +++ b/src/game/Game.cpp @@ -24,6 +24,20 @@ void GAMELOOP_RequestLevelChangeByName(char* name, GameTracker* gameTracker, int Hooking::Call(addr, name, gameTracker, doneType); } +bool GAMELOOP_IsWipeDone(int type) +{ + auto addr = GET_ADDRESS(0x450310, 0x452970, 0x52E880); + + return Hooking::CallReturn(addr, type); +} + +void GAMELOOP_SetScreenWipe(int type, int target, int time) +{ + auto addr = GET_ADDRESS(0x4503D0, 0x452A30, 0x52E8B0); + + Hooking::Call(addr, type, target, time); +} + void PLAYER_DebugSwitchPlayerCharacter() { auto addr = GET_ADDRESS(0x5A40B0, 0x5A39A0, 0x79DB50); diff --git a/src/game/Game.h b/src/game/Game.h index 73e2447..e9f777a 100644 --- a/src/game/Game.h +++ b/src/game/Game.h @@ -164,6 +164,8 @@ class Game }; void GAMELOOP_RequestLevelChangeByName(char* name, GameTracker* gameTracker, int doneType); +bool GAMELOOP_IsWipeDone(int type); +void GAMELOOP_SetScreenWipe(int type, int target, int time); void PLAYER_DebugSwitchPlayerCharacter(); int OBTABLE_GetObjectID(char* name); diff --git a/src/modules/MainMenu.cpp b/src/modules/MainMenu.cpp index 44e8611..c97ef41 100644 --- a/src/modules/MainMenu.cpp +++ b/src/modules/MainMenu.cpp @@ -31,10 +31,10 @@ void MainMenu::OnDraw() BirthObject(object); } -#ifndef TR8 // Player if (ImGui::CollapsingHeader("Player")) { +#ifndef TR8 // Fill 'er up if (ImGui::Button("Fill 'er up")) { @@ -76,9 +76,11 @@ void MainMenu::OnDraw() *flags &= ~0x80; } } - } #endif + ImGui::Checkbox("No death fade", &m_noDeathFade); + } + // Time if (ImGui::CollapsingHeader("Time")) { diff --git a/src/modules/MainMenu.h b/src/modules/MainMenu.h index 0efb8d2..cef36a5 100644 --- a/src/modules/MainMenu.h +++ b/src/modules/MainMenu.h @@ -7,6 +7,7 @@ class MainMenu : public Module { private: bool m_switchPlayerNextFrame = false; + bool m_noDeathFade = false; Option m_noWatermark{ "NoWatermark", false }; @@ -14,6 +15,8 @@ class MainMenu : public Module void SwitchPlayerCharacter(char* name = nullptr); public: + bool IsNoDeathFade() { return m_noDeathFade; } + void OnDraw(); void OnFrame(); void OnLoop(); diff --git a/src/modules/Patches.cpp b/src/modules/Patches.cpp index d61c799..9ef6ee8 100644 --- a/src/modules/Patches.cpp +++ b/src/modules/Patches.cpp @@ -4,9 +4,11 @@ #include "Patches.h" #include "util/Hooking.h" #include "game/Game.h" +#include "MainMenu.h" // Instance of patches so we can get it in our hooks without calling GetModule static Patches* s_patches; +static MainMenu* s_menu; #ifndef TR8 // Original functions @@ -40,9 +42,40 @@ static void GAMELOOP_HandleScreenWipes() } #endif +// No death fade code +int(__stdcall* s_DeathState_Entry)(int player, int data); +void(__stdcall* s_DeathState_Process)(int player, int data); + +static int __stdcall DeathState_Entry(int player, int data) +{ + auto ret = s_DeathState_Entry(player, data); + + if (!s_menu->IsNoDeathFade()) + { + // We NOPed the original code, so we call the wipe manually + // like the game code does + if (GAMELOOP_IsWipeDone(10)) + { + // TODO refactor, 90 is not a constant value in the game code + GAMELOOP_SetScreenWipe(10, 100, 90); + } + } + + return ret; +} + +static void __stdcall DeathState_Process(int player, int data) +{ + if (!s_menu->IsNoDeathFade()) + { + s_DeathState_Process(player, data); + } +} + Patches::Patches() { s_patches = this; + s_menu = Hook::GetInstance().GetModule().get(); #ifndef TR8 if (m_disableIntro.GetValue()) @@ -53,8 +86,16 @@ Patches::Patches() // Insert hooks MH_CreateHook((void*)GET_ADDRESS(0x40CA80, 0x43AB40, 0x000000), RenderG2_MotionBlur, (void**)&s_RenderG2_MotionBlur); MH_CreateHook((void*)GET_ADDRESS(0x450430, 0x452A90, 0x000000), GAMELOOP_HandleScreenWipes, (void**)&s_GAMELOOP_HandleScreenWipes); - MH_EnableHook(MH_ALL_HOOKS); #endif + + // Insert DeathState hooks + MH_CreateHook((void*)GET_ADDRESS(0x55DEC0, 0x5581D0, 0x75AA50), DeathState_Entry, (void**)&s_DeathState_Entry); + MH_CreateHook((void*)GET_ADDRESS(0x56EC70, 0x5699C0, 0x75AF90), DeathState_Process, (void**)&s_DeathState_Process); + + // NOP the original death wipe code in DeathState::Entry + Hooking::Nop((void*)GET_ADDRESS(0x55E188, 0x5584DC, 0x75AEDE), 5); + + MH_EnableHook(MH_ALL_HOOKS); } void Patches::RemoveIntro()