diff --git a/Client/game_sa/CGameSA.cpp b/Client/game_sa/CGameSA.cpp index 918091d482..049cb21787 100644 --- a/Client/game_sa/CGameSA.cpp +++ b/Client/game_sa/CGameSA.cpp @@ -242,6 +242,7 @@ CGameSA::CGameSA() D3DResourceSystemSA::StaticSetHooks(); CVehicleSA::StaticSetHooks(); CCheckpointSA::StaticSetHooks(); + CHudSA::StaticSetHooks(); } CGameSA::~CGameSA() diff --git a/Client/game_sa/CGameSA.h b/Client/game_sa/CGameSA.h index 33731af858..f945fe5c20 100644 --- a/Client/game_sa/CGameSA.h +++ b/Client/game_sa/CGameSA.h @@ -190,6 +190,7 @@ class CGameSA : public CGame int32_t GetCountOfAllFileIDs() { return (*(char**)(0x5B8AFA + 2) - *(char**)(0x5B8B08 + 6)) / sizeof(CStreamingInfo); } DWORD GetSystemTime() { return *(DWORD*)0xB7CB84; } // CTimer::m_snTimeInMilliseconds + int GetSystemFrameCounter() { return *(int*)0xB7CB4C; } // CTimer::m_FrameCounter bool IsAtMenu() { return *(unsigned long*)0xBA677B != 0; } // FrontEndMenuManager + 0x33 diff --git a/Client/game_sa/CHudSA.cpp b/Client/game_sa/CHudSA.cpp index 6b9c2ff770..630438bab2 100644 --- a/Client/game_sa/CHudSA.cpp +++ b/Client/game_sa/CHudSA.cpp @@ -21,6 +21,16 @@ extern CGameSA* pGame; char szVehicleName[50] = {'\0'}; char szZoneName[50] = {'\0'}; +static ComponentProperties componentProperties; + +RsGlobal* CHudSA::rsGlobal = reinterpret_cast(VAR_RSGlobal); +std::int16_t* CHudSA::itemToFlash = reinterpret_cast(VAR_ItemToFlash); + +float CHudSA::calcStreetchX = 0.0f; +float CHudSA::calcStreetchY = 0.0f; + +constexpr RwColor COLOR_BLACK = RwColor{0, 0, 0, 0}; + CHudSA::CHudSA() { InitComponentList(); @@ -33,6 +43,8 @@ CHudSA::CHudSA() m_pfAspectRatioMultiplicator = (float*)VAR_AspectRatioMult; MemPut(m_pfAspectRatioMultiplicator, 0.002232143f); + UpdateStreetchCalculations(); + // Patch xrefs to 0x863B34, because this variable seems to be shared (2 other functions without any context access to it; probably a compiler optimization) MemPut(0x58E7D4 + 2, (DWORD)&m_fSniperCrosshairScale); MemPut(0x58E7EA + 2, (DWORD)&m_fSniperCrosshairScale); @@ -40,6 +52,11 @@ CHudSA::CHudSA() MemPut(0x53E41A + 2, (DWORD)&m_fSniperCrosshairScale); MemPut(0x53E488 + 2, (DWORD)&m_fSniperCrosshairScale); MemPut(0x53E4BF + 2, (DWORD)&m_fSniperCrosshairScale); + + // Initalize default colors + componentProperties.hpBar.bar.fillColor = CHudSA::GetHUDColour(eHudColour::RED); + componentProperties.breathBar.bar.fillColor = CHudSA::GetHUDColour(eHudColour::LIGHT_BLUE); + componentProperties.armorBar.bar.fillColor = CHudSA::GetHUDColour(eHudColour::LIGHT_GRAY); } void CHudSA::Disable(bool bDisabled) @@ -69,9 +86,9 @@ void CHudSA::InitComponentList() SHudComponent componentList[] = { {1, HUD_AMMO, 1, FUNC_DrawAmmo, 1, 0xCC, 0xC3}, {1, HUD_WEAPON, 1, FUNC_DrawWeaponIcon, 1, 0xCC, 0xC3}, - {1, HUD_HEALTH, 1, FUNC_PrintHealthForPlayer, 1, 0xCC, 0xC3}, - {1, HUD_BREATH, 1, FUNC_PrintBreathForPlayer, 1, 0xCC, 0xC3}, - {1, HUD_ARMOUR, 1, FUNC_PrintArmourForPlayer, 1, 0xCC, 0xC3}, + {1, HUD_HEALTH, 1, FUNC_RenderHealthBar, 1, 0xCC, 0xC3}, + {1, HUD_BREATH, 1, FUNC_RenderBreathBar, 1, 0xCC, 0xC3}, + {1, HUD_ARMOUR, 1, FUNC_RenderArmorBar, 1, 0xCC, 0xC3}, {1, HUD_MONEY, 1, CODE_ShowMoney, 2, 0xCCCC, 0xE990}, {1, HUD_VEHICLE_NAME, 1, FUNC_DrawVehicleName, 1, 0xCC, 0xC3}, {1, HUD_AREA_NAME, 1, FUNC_DrawAreaName, 1, 0xCC, 0xC3}, @@ -150,6 +167,27 @@ bool CHudSA::IsComponentVisible(eHudComponent component) return false; } +void CHudSA::UpdateStreetchCalculations() +{ + calcStreetchX = rsGlobal->maximumWidth * (*reinterpret_cast(VAR_AspectRatioMultX)); + calcStreetchY = rsGlobal->maximumHeight * (*m_pfAspectRatioMultiplicator); + + ComponentPlacement& hpPlacement = componentProperties.hpBar.bar.placement; + hpPlacement.height = calcStreetchY * 9.0f; + hpPlacement.width = calcStreetchX * 109.0f; + hpPlacement.setDefaultXY = false; + + ComponentPlacement& breathPlacement = componentProperties.breathBar.bar.placement; + breathPlacement.height = calcStreetchY * 9.0f; + breathPlacement.width = calcStreetchX * 62.0f; + breathPlacement.setDefaultXY = false; + + ComponentPlacement& armorPlacement = componentProperties.armorBar.bar.placement; + armorPlacement.height = calcStreetchY * 9.0f; + armorPlacement.width = calcStreetchX * 62.0f; + armorPlacement.setDefaultXY = false; +} + // // CHudSA::AdjustComponents // @@ -165,6 +203,8 @@ void CHudSA::AdjustComponents(float fAspectRatio) // Set the camera crosshair scale (same display flaw as in #7659) MemPut(m_pfCameraCrosshairScale, 192.0f * (4.0f / 3.0f) / fAspectRatio); + + UpdateStreetchCalculations(); } // @@ -176,6 +216,8 @@ void CHudSA::ResetComponentAdjustment() MemPut(m_pfAspectRatioMultiplicator, 0.002232143f); MemPut(m_pfCameraCrosshairScale, 192.0f); m_fSniperCrosshairScale = 210.0f; + + UpdateStreetchCalculations(); } bool CHudSA::IsCrosshairVisible() @@ -228,3 +270,312 @@ bool CHudSA::IsCrosshairVisible() std::uint8_t crossHairType = *reinterpret_cast(VAR_CTheScripts_bDrawCrossHair); return specialAiming || simpleAiming || crossHairType > 0; } + +RwColor CHudSA::GetHUDColour(const eHudColour& colour) +{ + switch (colour) + { + case eHudColour::RED: + return {180, 25, 29, 255}; + case eHudColour::GREEN: + return {54, 104, 44, 255}; + case eHudColour::DARK_BLUE: + return {50, 60, 127, 255}; + case eHudColour::LIGHT_BLUE: + return {172, 203, 241, 255}; + case eHudColour::LIGHT_GRAY: + return {225, 225, 225, 255}; + case eHudColour::BLACK: + return {0, 0, 0, 255}; + case eHudColour::GOLD: + return {144, 98, 16, 255}; + case eHudColour::PURPLE: + return {168, 110, 252, 255}; + case eHudColour::DARK_GRAY: + return {150, 150, 150, 255}; + case eHudColour::DARK_RED: + return {104, 15, 17, 255}; + case eHudColour::DARK_GREEN: + return {38, 71, 31, 255}; + case eHudColour::CREAM: + return {226, 192, 99, 255}; + case eHudColour::NIGHT_BLUE: + return {74, 90, 107, 255}; + case eHudColour::BLUE: + return {20, 25, 200, 255}; + case eHudColour::YELLOW: + return {255, 255, 0, 255}; + default: + return {0, 0, 0, 255}; + } +} + +HudBar& CHudSA::GetHudBarRef(const eHudComponent& component) noexcept +{ + switch (component) + { + case HUD_HEALTH: + return componentProperties.hpBar.bar; + case HUD_BREATH: + return componentProperties.breathBar.bar; + case HUD_ARMOUR: + return componentProperties.armorBar.bar; + } +} + +void CHudSA::SetComponentPlacementPosition(ComponentPlacement& placement, const CVector2D& position) +{ + placement.customX = position.fX; + placement.customY = position.fY; + placement.useCustomPosition = true; +} + +void CHudSA::SetComponentPlacementSize(ComponentPlacement& placement, const CVector2D& size) +{ + placement.customWidth = size.fX; + placement.customHeight = size.fY; + placement.useCustomSize = true; +} + +void CHudSA::SetComponentPosition(const eHudComponent& component, const CVector2D& position) noexcept +{ + switch (component) + { + case HUD_BREATH: + SetComponentPlacementPosition(componentProperties.breathBar.bar.placement, position); + break; + case HUD_HEALTH: + SetComponentPlacementPosition(componentProperties.hpBar.bar.placement, position); + break; + case HUD_ARMOUR: + SetComponentPlacementPosition(componentProperties.armorBar.bar.placement, position); + break; + } +} + +void CHudSA::SetComponentSize(const eHudComponent& component, const CVector2D& size) noexcept +{ + switch (component) + { + case HUD_BREATH: + SetComponentPlacementSize(componentProperties.breathBar.bar.placement, size); + break; + case HUD_HEALTH: + SetComponentPlacementSize(componentProperties.hpBar.bar.placement, size); + break; + case HUD_ARMOUR: + SetComponentPlacementSize(componentProperties.armorBar.bar.placement, size); + break; + } +} + +void CHudSA::ResetComponent(ComponentPlacement& placement, bool resetSize) noexcept +{ + if (resetSize) + { + placement.useCustomSize = false; + placement.customHeight = 0.0f; + placement.customWidth = 0.0f; + } + else + { + placement.useCustomPosition = false; + placement.customX = 0.0f; + placement.customY = 0.0f; + } +} + +void CHudSA::ResetComponentPlacement(const eHudComponent& component, bool resetSize) noexcept +{ + switch (component) + { + case HUD_ALL: + { + for (const auto& cmp : m_HudComponentMap) + { + if (cmp.first == HUD_ALL) + continue; + + ResetComponentPlacement(cmp.first, resetSize); + } + + break; + } + case HUD_HEALTH: + ResetComponent(componentProperties.hpBar.bar.placement, resetSize); + break; + case HUD_BREATH: + ResetComponent(componentProperties.breathBar.bar.placement, resetSize); + break; + case HUD_ARMOUR: + ResetComponent(componentProperties.armorBar.bar.placement, resetSize); + break; + } +} + +void CHudSA::SetComponentBarColor(const eHudComponent& component, float color) noexcept +{ + SColor newColor = TOCOLOR2SCOLOR(static_cast(color)); + GetHudBarRef(component).fillColor = RwColor{newColor.R, newColor.G, newColor.B, newColor.A}; +} + +void CHudSA::SetComponentDrawBlackBorder(const eHudComponent& component, bool draw) noexcept +{ + GetHudBarRef(component).drawBlackBorder = draw; +} + +void CHudSA::SetComponentDrawPercentage(const eHudComponent& component, bool draw) noexcept +{ + GetHudBarRef(component).drawPercentage = draw; +} + +void CHudSA::SetHealthBarBlinkingValue(float minHealth) noexcept +{ + componentProperties.hpBar.blinkingBarHP = minHealth; +} + +void CHudSA::RenderHealthBar(int x, int y) +{ + // Flash each 8 frames + bool isValidFrame = (pGame->GetSystemFrameCounter() & 8) == 0; + if (*itemToFlash == 4 && isValidFrame) // 4 = HEALTH_BAR + return; + + CPed* playerPed = pGame->GetPedContext(); + if (!playerPed || (playerPed->GetHealth() <= componentProperties.hpBar.blinkingBarHP && isValidFrame)) + return; + + // Save default position once + if (!componentProperties.hpBar.bar.placement.setDefaultXY) + { + componentProperties.hpBar.bar.placement.x = x; + componentProperties.hpBar.bar.placement.y = y; + componentProperties.hpBar.bar.placement.setDefaultXY = true; + } + + // Get player max health + float maxHealth = static_cast(pGame->GetPlayerInfo()->GetMaxHealth()); + + // Use custom position/size? + bool useCustomPosition = componentProperties.hpBar.bar.placement.useCustomPosition; + bool useCustomSize = componentProperties.hpBar.bar.placement.useCustomSize; + + // Calc bar width depending on MAX_HEALTH stat + double statModifier = ((double(__cdecl*)(int))FUNC_CStats_GetFatAndMuscleModifier)(10); + float totalWidth = ((useCustomSize ? componentProperties.hpBar.bar.placement.customWidth : componentProperties.hpBar.bar.placement.width) * maxHealth) / statModifier; + + // call CSprite2d::DrawBarChart + ((void(__cdecl*)(float, float, std::uint16_t, std::uint32_t, float, bool, bool, bool, RwColor, RwColor))FUNC_CSprite2d_DrawBarChart)(useCustomPosition ? componentProperties.hpBar.bar.placement.customX : (useCustomSize ? componentProperties.hpBar.bar.placement.customWidth : componentProperties.hpBar.bar.placement.width) - totalWidth + x, useCustomPosition ? componentProperties.hpBar.bar.placement.customY : y, static_cast(totalWidth), static_cast(useCustomSize ? componentProperties.hpBar.bar.placement.customHeight : componentProperties.hpBar.bar.placement.height), playerPed->GetHealth() * 100.0f / maxHealth, false, componentProperties.hpBar.bar.drawPercentage, componentProperties.hpBar.bar.drawBlackBorder, componentProperties.hpBar.bar.fillColor, COLOR_BLACK); +} + +void CHudSA::RenderBreathBar(int x, int y) +{ + // Flash each 8 frames + if (*itemToFlash == 10 && (pGame->GetSystemFrameCounter() & 8) == 0) // 10 = BREATH_BAR + return; + + CPed* playerPed = pGame->GetPedContext(); + if (!playerPed) + return; + + // Save default position once + if (!componentProperties.breathBar.bar.placement.setDefaultXY) + { + componentProperties.breathBar.bar.placement.x = x; + componentProperties.breathBar.bar.placement.y = y; + componentProperties.breathBar.bar.placement.setDefaultXY = true; + } + + // Calc bar width depending on AIR_IN_LUNG stat + double statModifier = ((double(__cdecl*)(int))FUNC_CStats_GetFatAndMuscleModifier)(8); + + // Use custom position/size? + bool useCustomPosition = componentProperties.breathBar.bar.placement.useCustomPosition; + bool useCustomSize = componentProperties.breathBar.bar.placement.useCustomSize; + + // call CSprite2d::DrawBarChart + ((void(__cdecl*)(float, float, std::uint16_t, std::uint32_t, float, bool, bool, bool, RwColor, RwColor))FUNC_CSprite2d_DrawBarChart)(useCustomPosition ? componentProperties.breathBar.bar.placement.customX : x, useCustomPosition ? componentProperties.breathBar.bar.placement.customY : y, static_cast(useCustomSize ? componentProperties.breathBar.bar.placement.customWidth : componentProperties.breathBar.bar.placement.width), static_cast(useCustomSize ? componentProperties.breathBar.bar.placement.customHeight : componentProperties.breathBar.bar.placement.height), playerPed->GetOxygenLevel() / statModifier * 100.0f, false, componentProperties.breathBar.bar.drawPercentage, componentProperties.breathBar.bar.drawBlackBorder, componentProperties.breathBar.bar.fillColor, COLOR_BLACK); +} + +void CHudSA::RenderArmorBar(int x, int y) +{ + // Flash each 8 frames + if (*itemToFlash == 3 && (pGame->GetSystemFrameCounter() & 8) == 0) // 3 = ARMOR_BAR + return; + + CPed* playerPed = pGame->GetPedContext(); + if (!playerPed || playerPed->GetArmor() < 1.0f) + return; + + // Save default position once + if (!componentProperties.armorBar.bar.placement.setDefaultXY) + { + componentProperties.armorBar.bar.placement.x = x; + componentProperties.armorBar.bar.placement.y = y; + componentProperties.armorBar.bar.placement.setDefaultXY = true; + } + + // Use custom position/size? + bool useCustomPosition = componentProperties.hpBar.bar.placement.useCustomPosition; + bool useCustomSize = componentProperties.hpBar.bar.placement.useCustomSize; + + // call CSprite2d::DrawBarChart + ((void(__cdecl*)(float, float, std::uint16_t, std::uint32_t, float, bool, bool, bool, RwColor, RwColor))FUNC_CSprite2d_DrawBarChart)(useCustomPosition ? componentProperties.armorBar.bar.placement.customX : x, useCustomPosition ? componentProperties.armorBar.bar.placement.customY : y, static_cast(useCustomSize ? componentProperties.armorBar.bar.placement.customWidth : componentProperties.armorBar.bar.placement.width), static_cast(useCustomSize ? componentProperties.armorBar.bar.placement.customHeight : componentProperties.armorBar.bar.placement.height), playerPed->GetArmor() / static_cast(pGame->GetPlayerInfo()->GetMaxArmor()) * 100.0f, false, componentProperties.armorBar.bar.drawPercentage, componentProperties.armorBar.bar.drawBlackBorder, componentProperties.armorBar.bar.fillColor, COLOR_BLACK); +} + +static void _declspec(naked) HOOK_RenderHudBar() +{ + _asm + { + mov eax, [esp] + + push [esp+0Ch] // y + push [esp+0Ch] // x + + // Health bar + cmp eax, 0058EE9Fh + jz renderHealthBar + + cmp eax, 0058EF12h + jz renderHealthBar + + // Breath bar + cmp eax, 0058F136h + jz renderBreathBar + + cmp eax, 0058F1B2h + jz renderBreathBar + + // Armor bar + cmp eax, 0058EF70h + jz renderArmorBar + + cmp eax, 0058EFE3h + jz renderArmorBar + + jmp skip + + renderHealthBar: + call CHudSA::RenderHealthBar + jmp skip + + renderBreathBar: + call CHudSA::RenderBreathBar + jmp skip + + renderArmorBar: + call CHudSA::RenderArmorBar + jmp skip + + skip: + add esp, 8 + retn + } +} + +void CHudSA::StaticSetHooks() +{ + HookInstall(FUNC_RenderHealthBar, &HOOK_RenderHudBar, 11); + HookInstall(FUNC_RenderBreathBar, &HOOK_RenderHudBar, 11); + HookInstall(FUNC_RenderArmorBar, &HOOK_RenderHudBar, 11); +} diff --git a/Client/game_sa/CHudSA.h b/Client/game_sa/CHudSA.h index 11f1747b51..fd99e64bd3 100644 --- a/Client/game_sa/CHudSA.h +++ b/Client/game_sa/CHudSA.h @@ -13,19 +13,25 @@ #include #include +#include #define FUNC_Draw 0x58FAE0 #define VAR_DisableClock 0xBAA400 +// X +#define VAR_AspectRatioMultX 0x859520 +// Y #define VAR_AspectRatioMult 0x859524 + #define VAR_CameraCrosshairScale 0x866C74 #define FUNC_DrawAmmo 0x5893B0 #define FUNC_DrawWeaponIcon 0x58D7D0 -#define FUNC_PrintHealthForPlayer 0x589270 -#define FUNC_PrintBreathForPlayer 0x589190 -#define FUNC_PrintArmourForPlayer 0x5890A0 +#define FUNC_RenderHealthBar 0x589270 +#define FUNC_RenderBreathBar 0x589190 +#define FUNC_RenderArmorBar 0x5890A0 + #define FUNC_DrawVitalStats 0x589650 #define FUNC_DrawVehicleName 0x58AEA0 #define FUNC_DrawHelpText 0x58B6E0 @@ -36,9 +42,14 @@ #define FUNC_DrawWantedLevel 0x58D9A0 #define FUNC_DrawCrosshair 0x58E020 +#define FUNC_CStats_GetFatAndMuscleModifier 0x559AF0 +#define FUNC_CSprite2d_DrawBarChart 0x728640 + #define CODE_ShowMoney 0x58F47D #define VAR_CTheScripts_bDrawCrossHair 0xA44490 +#define VAR_RSGlobal 0xC17040 +#define VAR_ItemToFlash 0xBAB1DC struct SHudComponent { @@ -51,6 +62,84 @@ struct SHudComponent DWORD disabledData; }; +enum class eHudColour +{ + RED, + GREEN, + DARK_BLUE, + LIGHT_BLUE, + LIGHT_GRAY, + BLACK, + GOLD, + PURPLE, + DARK_GRAY, + DARK_RED, + DARK_GREEN, + CREAM, + NIGHT_BLUE, + BLUE, + YELLOW, +}; + +struct RsGlobal +{ + const char* appName; + std::int32_t maximumWidth; + std::int32_t maximumHeight; + std::int32_t frameLimit; + bool quit; + void* ps; + std::uint8_t keyboard[12]; // RsInputDevice + std::uint8_t mouse[12]; // RsInputDevice + std::uint8_t pad[12]; // RsInputDevice +}; + +struct ComponentPlacement +{ + // Original position & size + float x{0.0f}, y{0.0f}; // for getter function only + float width{0.0f}, height{0.0f}; + + // Custom position & size + float customX{0.0f}, customY{0.0f}; + float customWidth{0.0f}, customHeight{0.0f}; + + bool useCustomPosition{false}; + bool useCustomSize{false}; + bool setDefaultXY{false}; +}; + +struct HudBar +{ + ComponentPlacement placement; + RwColor fillColor{}; + bool drawBlackBorder{true}; + bool drawPercentage{false}; +}; + +struct HealthBar +{ + HudBar bar; + float blinkingBarHP{10.0f}; +}; + +struct BreathBar +{ + HudBar bar; +}; + +struct ArmorBar +{ + HudBar bar; +}; + +struct ComponentProperties +{ + HealthBar hpBar; + BreathBar breathBar; + ArmorBar armorBar; +}; + class CHudSA : public CHud { public: @@ -63,12 +152,44 @@ class CHudSA : public CHud void ResetComponentAdjustment(); bool IsCrosshairVisible(); + void SetComponentPlacementPosition(ComponentPlacement& placement, const CVector2D& position); + void SetComponentPlacementSize(ComponentPlacement& placement, const CVector2D& size); + + void SetComponentPosition(const eHudComponent& component, const CVector2D& position) noexcept override; + void SetComponentSize(const eHudComponent& component, const CVector2D& size) noexcept override; + + void ResetComponentPlacement(const eHudComponent& component, bool resetSize) noexcept override; + + void SetComponentBarColor(const eHudComponent& component, float color) noexcept override; + void SetComponentDrawBlackBorder(const eHudComponent& component, bool draw) noexcept override; + void SetComponentDrawPercentage(const eHudComponent& component, bool draw) noexcept override; + void SetHealthBarBlinkingValue(float minHealth) noexcept override; + + static RsGlobal* GetRSGlobal() noexcept { return rsGlobal; } + static RwColor GetHUDColour(const eHudColour& colour); + + static void StaticSetHooks(); + protected: void InitComponentList(); + void UpdateStreetchCalculations(); + void ResetComponent(ComponentPlacement& placement, bool resetSize) noexcept; + + static void RenderHealthBar(int x, int y); + static void RenderBreathBar(int x, int y); + static void RenderArmorBar(int x, int y); + + static HudBar& GetHudBarRef(const eHudComponent& component) noexcept; std::map m_HudComponentMap; float* m_pfAspectRatioMultiplicator; float* m_pfCameraCrosshairScale; float m_fSniperCrosshairScale; + + static RsGlobal* rsGlobal; + static std::int16_t* itemToFlash; + + static float calcStreetchX; + static float calcStreetchY; }; diff --git a/Client/game_sa/CPlayerInfoSA.h b/Client/game_sa/CPlayerInfoSA.h index fd73385f05..057c2927ab 100644 --- a/Client/game_sa/CPlayerInfoSA.h +++ b/Client/game_sa/CPlayerInfoSA.h @@ -268,4 +268,6 @@ class CPlayerInfoSA : public CPlayerInfo float GetBikeRearWheelDist() { return internalInterface->fBikeRearWheelDist; } DWORD GetBikeFrontWheelCounter() { return internalInterface->nBikeFrontWheelCounter; } float GetBikeFrontWheelDist() { return internalInterface->fBikeFrontWheelDist; } + std::uint8_t GetMaxHealth() { return internalInterface->MaxHealth; } + std::uint8_t GetMaxArmor() { return internalInterface->MaxArmour; } }; diff --git a/Client/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.cpp b/Client/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.cpp index 64708dea0c..ace6ec2010 100644 --- a/Client/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.cpp +++ b/Client/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.cpp @@ -111,6 +111,15 @@ ADD_ENUM(HUD_CROSSHAIR, "crosshair") ADD_ENUM(HUD_ALL, "all") IMPLEMENT_ENUM_END("hud-component") +IMPLEMENT_ENUM_CLASS_BEGIN(eHudComponentProperty) +ADD_ENUM(eHudComponentProperty::POSITION, "position") +ADD_ENUM(eHudComponentProperty::SIZE, "size") +ADD_ENUM(eHudComponentProperty::FILL_COLOR, "fillColor") +ADD_ENUM(eHudComponentProperty::DRAW_BLACK_BORDER, "drawBlackBorder") +ADD_ENUM(eHudComponentProperty::DRAW_PERCENTAGE, "drawPercentage") +ADD_ENUM(eHudComponentProperty::BLINKING_HP_VALUE, "blinkingValue") +IMPLEMENT_ENUM_CLASS_END("hud-component-property") + IMPLEMENT_ENUM_BEGIN(eAmbientSoundType) ADD_ENUM(AMBIENT_SOUND_GENERAL, "general") ADD_ENUM(AMBIENT_SOUND_GUNFIRE, "gunfire") diff --git a/Client/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.h b/Client/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.h index e134a73bea..fffe9810b5 100644 --- a/Client/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.h +++ b/Client/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.h @@ -108,6 +108,7 @@ enum eDXVerticalAlign DECLARE_ENUM(eDXVerticalAlign); DECLARE_ENUM(eHudComponent); +DECLARE_ENUM_CLASS(eHudComponentProperty); enum eFieldOfViewMode { diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaPlayerDefs.cpp b/Client/mods/deathmatch/logic/luadefs/CLuaPlayerDefs.cpp index e276a3ddf7..4b4247c9f3 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaPlayerDefs.cpp +++ b/Client/mods/deathmatch/logic/luadefs/CLuaPlayerDefs.cpp @@ -40,6 +40,8 @@ void CLuaPlayerDefs::LoadFunctions() {"setPlayerNametagText", SetPlayerNametagText}, {"setPlayerNametagColor", SetPlayerNametagColor}, {"setPlayerNametagShowing", SetPlayerNametagShowing}, + {"setPlayerHudComponentProperty", ArgumentParser}, + {"resetPlayerHudComponentProperty", ArgumentParser}, // Community funcs {"getPlayerUserName", GetPlayerUserName}, @@ -77,6 +79,8 @@ void CLuaPlayerDefs::AddClass(lua_State* luaVM) lua_classfunction(luaVM, "isMapVisible", "isPlayerMapVisible"); lua_classfunction(luaVM, "isHudComponentVisible", "isPlayerHudComponentVisible"); lua_classfunction(luaVM, "toggleControl", "toggleControl"); + lua_classfunction(luaVM, "setHudComponentProperty", "setPlayerHudComponentProperty"); + lua_classfunction(luaVM, "resetHudComponentProperty", "resetPlayerHudComponentProperty"); lua_classfunction(luaVM, "create", "getPlayerFromName"); @@ -644,3 +648,168 @@ bool CLuaPlayerDefs::IsPlayerCrosshairVisible() { return g_pGame->GetHud()->IsCrosshairVisible(); } + +bool CLuaPlayerDefs::SetPlayerHudComponentProperty(eHudComponent component, eHudComponentProperty property, std::variant value) +{ + switch (property) + { + case eHudComponentProperty::POSITION: + { + if (!std::holds_alternative(value)) + return false; + + g_pGame->GetHud()->SetComponentPosition(component, std::get(value)); + return true; + } + case eHudComponentProperty::SIZE: + { + if (!std::holds_alternative(value)) + return false; + + g_pGame->GetHud()->SetComponentSize(component, std::get(value)); + return true; + } + case eHudComponentProperty::FILL_COLOR: + { + switch (component) + { + case HUD_HEALTH: + case HUD_ARMOUR: + case HUD_BREATH: + { + if (!std::holds_alternative(value)) + return false; + + g_pGame->GetHud()->SetComponentBarColor(component, std::get(value)); + return true; + } + } + + break; + } + case eHudComponentProperty::DRAW_BLACK_BORDER: + { + switch (component) + { + case HUD_HEALTH: + case HUD_ARMOUR: + case HUD_BREATH: + { + if (!std::holds_alternative(value)) + return false; + + g_pGame->GetHud()->SetComponentDrawBlackBorder(component, std::get(value)); + return true; + } + } + + break; + } + case eHudComponentProperty::DRAW_PERCENTAGE: + { + switch (component) + { + case HUD_HEALTH: + case HUD_ARMOUR: + case HUD_BREATH: + { + if (!std::holds_alternative(value)) + return false; + + g_pGame->GetHud()->SetComponentDrawPercentage(component, std::get(value)); + return true; + } + } + + break; + } + case eHudComponentProperty::BLINKING_HP_VALUE: + { + if (component != HUD_HEALTH) + return false; + + if (!std::holds_alternative(value)) + return false; + + g_pGame->GetHud()->SetHealthBarBlinkingValue(std::get(value)); + return true; + } + } + + return false; +} + +bool CLuaPlayerDefs::ResetPlayerHudComponentProperty(eHudComponent component, eHudComponentProperty property) +{ + switch (property) + { + case eHudComponentProperty::POSITION: + { + g_pGame->GetHud()->ResetComponentPlacement(component, false); + return true; + } + case eHudComponentProperty::SIZE: + { + g_pGame->GetHud()->ResetComponentPlacement(component, true); + return true; + } + case eHudComponentProperty::FILL_COLOR: + { + switch (component) + { + case HUD_HEALTH: + // eHudColour::RED + g_pGame->GetHud()->SetComponentBarColor(component, COLOR_RGBA(180, 25, 29, 255)); + return true; + case HUD_BREATH: + // eHudColour::LIGHT_BLUE + g_pGame->GetHud()->SetComponentBarColor(component, COLOR_RGBA(172, 203, 241, 255)); + return true; + case HUD_ARMOUR: + // eHudColour::LIGHT_GRAY + g_pGame->GetHud()->SetComponentBarColor(component, COLOR_RGBA(225, 225, 225, 255)); + return true; + } + + break; + } + case eHudComponentProperty::DRAW_BLACK_BORDER: + { + switch (component) + { + case HUD_HEALTH: + case HUD_BREATH: + case HUD_ARMOUR: + g_pGame->GetHud()->SetComponentDrawBlackBorder(component, true); + return true; + } + + break; + } + case eHudComponentProperty::DRAW_PERCENTAGE: + { + switch (component) + { + case HUD_HEALTH: + case HUD_BREATH: + case HUD_ARMOUR: + g_pGame->GetHud()->SetComponentDrawPercentage(component, false); + return true; + } + + break; + } + case eHudComponentProperty::BLINKING_HP_VALUE: + { + if (component == HUD_HEALTH) + { + g_pGame->GetHud()->SetHealthBarBlinkingValue(10.0f); + return true; + } + + break; + } + } + + return false; +} diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaPlayerDefs.h b/Client/mods/deathmatch/logic/luadefs/CLuaPlayerDefs.h index 1a2895ed3d..8baca7900c 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaPlayerDefs.h +++ b/Client/mods/deathmatch/logic/luadefs/CLuaPlayerDefs.h @@ -41,6 +41,8 @@ class CLuaPlayerDefs : public CLuaDefs LUA_DECLARE(SetPlayerNametagText); LUA_DECLARE(SetPlayerNametagColor); LUA_DECLARE(SetPlayerNametagShowing); + static bool SetPlayerHudComponentProperty(eHudComponent component, eHudComponentProperty property, std::variant value); + static bool ResetPlayerHudComponentProperty(eHudComponent component, eHudComponentProperty property); // Community funcs LUA_DECLARE(GetPlayerUserName); diff --git a/Client/sdk/game/CGame.h b/Client/sdk/game/CGame.h index 06866e06ce..837a2ce034 100644 --- a/Client/sdk/game/CGame.h +++ b/Client/sdk/game/CGame.h @@ -156,6 +156,7 @@ class __declspec(novtable) CGame virtual CModelInfo* GetModelInfo(DWORD dwModelID, bool bCanBeInvalid = false) = 0; virtual DWORD GetSystemTime() = 0; + virtual int GetSystemFrameCounter() = 0; virtual bool IsAtMenu() = 0; virtual void StartGame() = 0; virtual void SetSystemState(eSystemState State) = 0; diff --git a/Client/sdk/game/CHud.h b/Client/sdk/game/CHud.h index af37d4a4cd..208a6e8a37 100644 --- a/Client/sdk/game/CHud.h +++ b/Client/sdk/game/CHud.h @@ -10,6 +10,7 @@ *****************************************************************************/ #pragma once +#include "CVector2D.h" enum eHudComponent { @@ -32,6 +33,16 @@ enum eHudComponent HUD_HELP_TEXT, }; +enum class eHudComponentProperty +{ + FILL_COLOR, + DRAW_BLACK_BORDER, + DRAW_PERCENTAGE, + BLINKING_HP_VALUE, + POSITION, + SIZE, +}; + class CHud { public: @@ -42,4 +53,13 @@ class CHud virtual void AdjustComponents(float fAspectRatio) = 0; virtual void ResetComponentAdjustment() = 0; virtual bool IsCrosshairVisible() = 0; + + // Hud properties + virtual void SetComponentBarColor(const eHudComponent& component, float color) noexcept = 0; + virtual void SetComponentDrawBlackBorder(const eHudComponent& component, bool draw) noexcept = 0; + virtual void SetComponentDrawPercentage(const eHudComponent& component, bool draw) noexcept = 0; + virtual void SetHealthBarBlinkingValue(float minHealth) noexcept = 0; + virtual void SetComponentPosition(const eHudComponent& component, const CVector2D& position) noexcept = 0; + virtual void SetComponentSize(const eHudComponent& component, const CVector2D& size) noexcept = 0; + virtual void ResetComponentPlacement(const eHudComponent& component, bool resetSize) noexcept = 0; }; diff --git a/Client/sdk/game/CPlayerInfo.h b/Client/sdk/game/CPlayerInfo.h index 91b7e16d6c..44f0e08787 100644 --- a/Client/sdk/game/CPlayerInfo.h +++ b/Client/sdk/game/CPlayerInfo.h @@ -38,4 +38,6 @@ class CPlayerInfo virtual float GetBikeRearWheelDist() = 0; virtual DWORD GetBikeFrontWheelCounter() = 0; virtual float GetBikeFrontWheelDist() = 0; + virtual std::uint8_t GetMaxHealth() = 0; + virtual std::uint8_t GetMaxArmor() = 0; }; diff --git a/Shared/sdk/SharedUtil.Defines.h b/Shared/sdk/SharedUtil.Defines.h index 580335e61b..bccd2e8f2f 100644 --- a/Shared/sdk/SharedUtil.Defines.h +++ b/Shared/sdk/SharedUtil.Defines.h @@ -166,6 +166,8 @@ #define ZERO_POD_STRUCT(ptr) \ memset ( ptr, 0, sizeof(*(ptr)) ) +#define mask(n) ((1 << (n)) - 1) + // printf/wprintf helpers // // http://www.firstobject.com/wchar_t-string-on-linux-osx-windows.htm diff --git a/Shared/sdk/SharedUtil.Misc.h b/Shared/sdk/SharedUtil.Misc.h index d64f155a7c..bd8f7bfe15 100644 --- a/Shared/sdk/SharedUtil.Misc.h +++ b/Shared/sdk/SharedUtil.Misc.h @@ -549,6 +549,20 @@ namespace SharedUtil inline SColor COLOR_ARGB(unsigned char A, unsigned char R, unsigned char G, unsigned char B) { return SColorRGBA(R, G, B, A); } inline SColor COLOR_ABGR(unsigned char A, unsigned char B, unsigned char G, unsigned char R) { return SColorRGBA(R, G, B, A); } + // + // Convert tocolor value to SColor + // + inline SColor TOCOLOR2SCOLOR(std::uint32_t colorValue) + { + SColor color; + color.R = static_cast((colorValue >> 16) & mask(8)); + color.G = static_cast((colorValue >> 8) & mask(8)); + color.B = static_cast((colorValue >> 0) & mask(8)); + color.A = static_cast((colorValue >> 24) & mask(8)); + + return color; + } + // // Cross platform critical section //