VC/SA: Add Minimal HUD to VC, document it in SA

This commit is contained in:
Silent 2024-10-12 20:28:14 +02:00
parent 8301635efc
commit 3d205cdf95
No known key found for this signature in database
GPG key ID: AE53149BB0C45AF1
4 changed files with 175 additions and 6 deletions

View file

@ -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.

View file

@ -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.

View file

@ -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<int32_t>( 0x588905 + 1, 0 );
}
if ( bHasDebugMenu )
{
static bool bMinimalHUDEnabled = INIoption == 1;
static bool bMinimalHUDEnabled = INIoption != 0;
DebugMenuAddVar( "SilentPatch", "Minimal HUD", &bMinimalHUDEnabled, []() {
if ( bMinimalHUDEnabled )
{

View file

@ -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<rwRENDERSTATEVERTEXALPHAENABLE> vertexAlpha;
RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE);
orgRenderOneXLUSprite(arg1, arg2, arg3, arg4, arg5, red, green, blue, mult, arg10, static_cast<uint8_t>(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<decltype(HUDReInitialise)>(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<uint32_t*, 4> hudInitialiseVariables = {
reinitialise1.get(2).get<uint32_t>(6),
reinitialise1.get(3).get<uint32_t>(6),
reinitialise1.get(4).get<uint32_t>(6),
get_pattern<uint32_t>("8B 83 ? ? ? ? C7 05 ? ? ? ? 05 00 00 00", 6 + 6),
};
static const std::array<uint32_t*, 4> hudReinitialiseVariables = {
reinitialise1.get(0).get<uint32_t>(6),
reinitialise1.get(1).get<uint32_t>(6),
reinitialise2.get(0).get<uint32_t>(6),
reinitialise2.get(1).get<uint32_t>(6),
};
ReadCall(setDropShadow.get<void>(2), orgSetDropShadowPosition);
ReadCall(setDropShadow.get<void>(39), orgSetDropColor);
pDropShadowSize = setDropShadow.get<int8_t>(1);
pDropShadowB = setDropShadow.get<int8_t>(20 + 1);
pDropShadowG = setDropShadow.get<int8_t>(22 + 1);
pDropShadowR = setDropShadow.get<int8_t>(24 + 1);
InterceptCall(setColor_WantedStars, orgSetColor, SetColor_SetShadow);
pPlayerInFocus = *timeLastArmorLoss.get<uint8_t*>(3);
pTimeLastArmorLoss = *timeLastArmorLoss.get<uint32_t*>(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<uint32_t>(var, 0);
}
for (uint32_t* var : hudReinitialiseVariables)
{
Patch<uint32_t>(var, 0);
}
}
if (bHasDebugMenu)
{
static bool bMinimalHUDEnabled = INIoption != 0;
DebugMenuAddVar("SilentPatch", "Minimal HUD", &bMinimalHUDEnabled, [] {
if (bMinimalHUDEnabled)
{
for (uint32_t* var : hudReinitialiseVariables)
{
Memory::VP::Patch<uint32_t>(var, 0);
}
}
else
{
for (uint32_t* var : hudReinitialiseVariables)
{
Memory::VP::Patch<uint32_t>(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