From 3d205cdf9596746f5fa9876369d7d52313290079 Mon Sep 17 00:00:00 2001 From: Silent Date: Sat, 12 Oct 2024 20:28:14 +0200 Subject: [PATCH] VC/SA: Add Minimal HUD to VC, document it in SA --- Config/SilentPatchSA.ini | 3 + Config/SilentPatchVC.ini | 3 + SilentPatchSA/SilentPatchSA.cpp | 12 +-- SilentPatchVC/SilentPatchVC.cpp | 163 ++++++++++++++++++++++++++++++++ 4 files changed, 175 insertions(+), 6 deletions(-) diff --git a/Config/SilentPatchSA.ini b/Config/SilentPatchSA.ini index ea7a55a..a89a22a 100644 --- a/Config/SilentPatchSA.ini +++ b/Config/SilentPatchSA.ini @@ -27,6 +27,9 @@ TrueInvincibility=0 ; Allows to override units used by the game - 0 forces metric units, 1 forces imperial units, ; -1 makes the game use units based on system locale Units=-1 +; Enables an unfinished "minimal HUD" feature, where health, armour, weapon and money UI elements +; fade out when inactive. +MinimalHUD=0 ; Enables the sliding mission title text, based on remnants of this feature in the game code. SlidingMissionTitleText=0 ; Enables the sliding odd job text, based on remnants of this feature in the game code. diff --git a/Config/SilentPatchVC.ini b/Config/SilentPatchVC.ini index 43d5dad..68d18db 100644 --- a/Config/SilentPatchVC.ini +++ b/Config/SilentPatchVC.ini @@ -7,6 +7,9 @@ Units=-1 ; it also adds a siren to FBI Washington, corrects taxi sign light placement on Taxi ; and fixes placement of the search light and rear rotor light on Police Maverick. EnableVehicleCoronaFixes=1 +; Enables an unfinished "minimal HUD" feature, where health, armour, weapon and money UI elements +; fade out when inactive. +MinimalHUD=0 ; Enables the sliding mission title text, based on remnants of this feature in the game code. SlidingMissionTitleText=0 ; Enables the sliding odd job text, based on remnants of this feature in the game code. diff --git a/SilentPatchSA/SilentPatchSA.cpp b/SilentPatchSA/SilentPatchSA.cpp index 1a7522d..62ab7ef 100644 --- a/SilentPatchSA/SilentPatchSA.cpp +++ b/SilentPatchSA/SilentPatchSA.cpp @@ -1457,8 +1457,8 @@ static int64_t AudioUtilsGetCurrentTimeInMs() return ((currentTime.QuadPart - UtilsStartTime.QuadPart) * 1000) / UtilsFrequency.QuadPart; } -// Minimal HUD changes -namespace MinimalHud +// ============= Minimal HUD changes ============= +namespace MinimalHUD { static CRGBA* __fastcall SetRGBA_FloatAlpha( CRGBA* rgba, void*, uint8_t red, uint8_t green, uint8_t blue, float alpha ) { @@ -4146,9 +4146,9 @@ BOOL InjectDelayedPatches_10() } // Minimal HUD - if ( const int INIoption = GetPrivateProfileIntW(L"SilentPatch", L"MinimalHud", -1, wcModulePath); INIoption != -1 ) + if ( const int INIoption = GetPrivateProfileIntW(L"SilentPatch", L"MinimalHUD", -1, wcModulePath); INIoption != -1 ) { - using namespace MinimalHud; + using namespace MinimalHUD; // Fix original bugs Patch( 0x58950E, { 0x90, 0xFF, 0x74, 0x24, 0x1C } ); @@ -4159,14 +4159,14 @@ BOOL InjectDelayedPatches_10() InjectHook( 0x58D8FD, &RenderXLUSprite_FloatAlpha ); // Re-enable - if ( INIoption == 1 ) + if ( INIoption != 0 ) { Patch( 0x588905 + 1, 0 ); } if ( bHasDebugMenu ) { - static bool bMinimalHUDEnabled = INIoption == 1; + static bool bMinimalHUDEnabled = INIoption != 0; DebugMenuAddVar( "SilentPatch", "Minimal HUD", &bMinimalHUDEnabled, []() { if ( bMinimalHUDEnabled ) { diff --git a/SilentPatchVC/SilentPatchVC.cpp b/SilentPatchVC/SilentPatchVC.cpp index f5fa5eb..a5f7f83 100644 --- a/SilentPatchVC/SilentPatchVC.cpp +++ b/SilentPatchVC/SilentPatchVC.cpp @@ -526,6 +526,59 @@ namespace SlidingTextsScalingFixes }; } + +// ============= Minimal HUD changes ============= +namespace MinimalHUD +{ + // Wanted level stars + static void (*orgSetDropShadowPosition)(short); + static void (*orgSetDropColor)(const CRGBA&); + + static int8_t* pDropShadowSize; + static int8_t* pDropShadowR; + static int8_t* pDropShadowG; + static int8_t* pDropShadowB; + + static void (*orgSetColor)(const CRGBA&); + static void SetColor_SetShadow(const CRGBA& color) + { + orgSetDropShadowPosition(*pDropShadowSize); + orgSetDropColor(CRGBA(*pDropShadowR, *pDropShadowG, *pDropShadowB, color.a)); + orgSetColor(color); + } + + + // Show the energy values when losing armor + static uint8_t* pPlayerInFocus; + static uint32_t* pTimeLastArmorLoss; + + static uint32_t LastTimeArmorLost; + + static float (*orgDrawFadeState)(int state, int force); + static float DrawFadeState_CheckArmor(int state, int force) + { + // This is a bit hacky, but we don't necessarily need better right now + if (pTimeLastArmorLoss[92 * *pPlayerInFocus] != LastTimeArmorLost) + { + force = 1; + LastTimeArmorLost = pTimeLastArmorLoss[92 * *pPlayerInFocus]; + } + return orgDrawFadeState(state, force); + } + + + // Fade the weapon icon - fix the render state and pass the alpha parameter + static void (*orgRenderOneXLUSprite)(float, float, float, float, float, uint8_t, uint8_t, uint8_t, int16_t, float, uint8_t); + static void RenderOneXLUSprite_FloatAlpha(float arg1, float arg2, float arg3, float arg4, float arg5, uint8_t red, uint8_t green, uint8_t blue, int16_t mult, float arg10, float alpha) + { + RwScopedRenderState vertexAlpha; + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); + + orgRenderOneXLUSprite(arg1, arg2, arg3, arg4, arg5, red, green, blue, mult, arg10, static_cast(alpha)); + } +} + + float FixedRefValue() { return 1.0f; @@ -1688,6 +1741,116 @@ void InjectDelayedPatches_VC_Common( bool bHasDebugMenu, const wchar_t* wcModule TXN_CATCH(); + // Minimal HUD + if (const int INIoption = GetPrivateProfileIntW(L"SilentPatch", L"MinimalHUD", -1, wcModulePath); INIoption != -1) try + { + using namespace MinimalHUD; + + // Fix original bugs + + // Wanted level stars losing their shadow if health/armour counters are off + auto setColor_WantedStars = get_pattern("E8 ? ? ? ? DB 05 ? ? ? ? 8D 84 24 ? ? ? ? 59 50 50 D8 0D ? ? ? ? D8 0D ? ? ? ? D9 1C 24 FF 74 24 24"); + + // Get SetDropShadowPosition and SetDropColor with their corresponding parameters straight from the armor counter, + // to preserve the current behaviour + auto setDropShadow = pattern("6A ? E8 ? ? ? ? 59 8D 8C 24 ? ? ? ? 68 ? ? ? ? 6A ? 6A ? 6A ? E8 ? ? ? ? 8D 84 24 ? ? ? ? 50 E8").get_one(); + + + // Show the energy values when losing armor + auto drawFadeState_Energy = get_pattern("6A 01 E8 ? ? ? ? DD D8", 2); + + auto timeLastArmorLoss = pattern("0F B6 15 ? ? ? ? A1 ? ? ? ? 6B D2 2E 89 04 D5 ? ? ? ? D9 83").get_one(); + + + // Fade the weapon icon - fix the render state and pass the alpha parameter + auto drawWeaponIconAlphaPush = get_pattern("68 FF 00 00 00 D8 0D ? ? ? ? FF 35"); + auto renderOneXLUSprite = get_pattern("E8 ? ? ? ? 83 C4 2C 6A 00 6A 08"); + + + // Stuff to let us (re)initialize + static void (*HUDReInitialise)() = static_cast(get_pattern("31 C0 53 0F EF C0 C6 05")); + + // This pattern has 5 hits - first 2 are in Reinitialise, the rest is in Initialise + auto reinitialise1 = pattern("C7 05 ? ? ? ? 05 00 00 00 66 C7 05 ? ? ? ? 00 00 C7 05 ? ? ? ? 00 00 00 00").count(5); + + // This one covers the rest of Reinitialise + auto reinitialise2 = pattern("C7 05 ? ? ? ? 05 00 00 00 C6 05 ? ? ? ? 00 C7 05 ? ? ? ? 00 00 00 00").count(2); + + // This one we touch only once, no need for static + const std::array hudInitialiseVariables = { + reinitialise1.get(2).get(6), + reinitialise1.get(3).get(6), + reinitialise1.get(4).get(6), + + get_pattern("8B 83 ? ? ? ? C7 05 ? ? ? ? 05 00 00 00", 6 + 6), + }; + + static const std::array hudReinitialiseVariables = { + reinitialise1.get(0).get(6), + reinitialise1.get(1).get(6), + + reinitialise2.get(0).get(6), + reinitialise2.get(1).get(6), + }; + + + ReadCall(setDropShadow.get(2), orgSetDropShadowPosition); + ReadCall(setDropShadow.get(39), orgSetDropColor); + pDropShadowSize = setDropShadow.get(1); + pDropShadowB = setDropShadow.get(20 + 1); + pDropShadowG = setDropShadow.get(22 + 1); + pDropShadowR = setDropShadow.get(24 + 1); + InterceptCall(setColor_WantedStars, orgSetColor, SetColor_SetShadow); + + + pPlayerInFocus = *timeLastArmorLoss.get(3); + pTimeLastArmorLoss = *timeLastArmorLoss.get(0xF + 3); + InterceptCall(drawFadeState_Energy, orgDrawFadeState, DrawFadeState_CheckArmor); + + + // push 0FFh -> push dword ptr [esp+520h+var_4E0] + Patch(drawWeaponIconAlphaPush, { 0x90, 0xFF, 0x74, 0x24, 0x40 }); + InterceptCall(renderOneXLUSprite, orgRenderOneXLUSprite, RenderOneXLUSprite_FloatAlpha); + + if (INIoption != 0) + { + for (uint32_t* var : hudInitialiseVariables) + { + Patch(var, 0); + } + for (uint32_t* var : hudReinitialiseVariables) + { + Patch(var, 0); + } + } + + if (bHasDebugMenu) + { + static bool bMinimalHUDEnabled = INIoption != 0; + DebugMenuAddVar("SilentPatch", "Minimal HUD", &bMinimalHUDEnabled, [] { + if (bMinimalHUDEnabled) + { + for (uint32_t* var : hudReinitialiseVariables) + { + Memory::VP::Patch(var, 0); + } + } + else + { + for (uint32_t* var : hudReinitialiseVariables) + { + Memory::VP::Patch(var, 5); + } + } + + // Call CHud::ReInitialise + HUDReInitialise(); + }); + } + } + TXN_CATCH(); + + // Fix some big messages staying on screen longer at high resolutions due to a cut sliding text feature // Also since we're touching it, optionally allow to re-enable this feature. try