diff --git a/mp/src/game/client/neo/ui/neo_hud_ammo.cpp b/mp/src/game/client/neo/ui/neo_hud_ammo.cpp index cbf13dab6..d77f3711a 100644 --- a/mp/src/game/client/neo/ui/neo_hud_ammo.cpp +++ b/mp/src/game/client/neo/ui/neo_hud_ammo.cpp @@ -14,9 +14,16 @@ #include "ienginevgui.h" #include "neo_hud_elements.h" +#include "inttostr.h" #include "ammodef.h" +#include "weapon_ghost.h" +#include "weapon_grenade.h" +#include "weapon_neobasecombatweapon.h" +#include "weapon_smokegrenade.h" +#include "weapon_supa7.h" + // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -26,10 +33,10 @@ using vgui::surface; ConVar neo_cl_hud_ammo_enabled("neo_cl_hud_ammo_enabled", "1", FCVAR_USERINFO, "Whether the HUD ammo is enabled or not.", true, 0, true, 1); -ConVar neo_cl_hud_ammo_pos_x("neo_cl_hud_ammo_pos_x", "15", FCVAR_USERINFO, - "HUD ammo X offset divisor.", true, 1, false, 100); -ConVar neo_cl_hud_ammo_pos_y("neo_cl_hud_ammo_pos_y", "20", FCVAR_USERINFO, - "HUD ammo Y offset divisor.", true, 1, false, 100); +ConVar neo_cl_hud_ammo_pos_x("neo_cl_hud_ammo_pos_x", "5", FCVAR_USERINFO, + "HUD ammo X offset divisor.", true, 1, false, 0); +ConVar neo_cl_hud_ammo_pos_y("neo_cl_hud_ammo_pos_y", "5", FCVAR_USERINFO, + "HUD ammo Y offset divisor.", true, 1, false, 0); ConVar neo_cl_hud_debug_ammo_color_r("neo_cl_hud_debug_ammo_color_r", "190", FCVAR_USERINFO | FCVAR_CHEAT, "Red color value of the ammo, in range 0 - 255.", true, 0.0f, true, 255.0f); @@ -68,8 +75,9 @@ CNEOHud_Ammo::CNEOHud_Ammo(const char* pElementName, vgui::Panel* parent) Error("CNEOHud_Ammo: Failed to load neoscheme\n"); } - m_hTextFont = scheme->GetFont("NHudOCRSmall"); + m_hSmallTextFont = scheme->GetFont("NHudOCRSmall"); m_hBulletFont = scheme->GetFont("NHudBullets"); + m_hTextFont = scheme->GetFont("NHudOCR"); SetVisible(neo_cl_hud_ammo_enabled.GetBool()); @@ -99,6 +107,12 @@ void CNEOHud_Ammo::ApplySchemeSettings(vgui::IScheme* pScheme) SetBounds(0, 0, m_resX, m_resY); SetRoundedCorners(PANEL_ROUND_CORNER_ALL); // FIXME + + wchar_t sampleText[1] = { 'a' }; + surface()->GetTextSize(m_hSmallTextFont, sampleText, m_smallFontWidth, m_smallFontHeight); + sampleText[0] = { 'h' }; + surface()->GetTextSize(m_hBulletFont, sampleText, m_bulletFontWidth, m_bulletFontHeight); + m_fontWidth = surface()->GetCharacterWidth(m_hTextFont, '4'); //Widest character } void CNEOHud_Ammo::DrawAmmo() const @@ -112,6 +126,7 @@ void CNEOHud_Ammo::DrawAmmo() const } const Color textColor = COLOR_WHITE; + auto textColorTransparent = Color(textColor.r(), textColor.g(), textColor.b(), 127); const size_t maxWepnameLen = 64; char wepName[maxWepnameLen]{ '\0' }; @@ -125,57 +140,156 @@ void CNEOHud_Ammo::DrawAmmo() const } g_pVGuiLocalize->ConvertANSIToUnicode(wepName, unicodeWepName, sizeof(unicodeWepName)); - int fontWidth, fontHeight; - surface()->GetTextSize(m_hTextFont, unicodeWepName, fontWidth, fontHeight); - + const int margin = GetMargin(); //Get this from scheme? + // These are the constant res based scalings of the NT ammo/health box dimensions. - const int xpos = m_resX - (m_resX * 0.2375); - const int ypos = m_resY - (m_resY * (0.1 / 1.5)); - - const int margin = neo_cl_hud_ammo_enabled.GetInt(); - DrawNeoHudRoundedBox(xpos - margin, ypos - margin, m_resX - margin, m_resY - margin); - - surface()->DrawSetTextFont(m_hTextFont); - surface()->DrawSetTextColor(textColor); - surface()->DrawSetTextPos(m_resX - fontWidth * 1.5 - margin, ypos + fontHeight * 0.5 - margin); + const int xPos1 = m_resX - neo_cl_hud_ammo_pos_x.GetInt(); + const int yPos1 = m_resY - neo_cl_hud_ammo_pos_y.GetInt(); + const int xPos0 = xPos1 - ((m_resX * 0.2375) + (margin * 2)); + const int yPos0 = yPos1 - (((m_bulletFontHeight * 0.8) + m_smallFontHeight + margin * 2) + (margin * 2)); + + + DrawNeoHudRoundedBox(xPos0, yPos0, xPos1, yPos1); + + surface()->DrawSetTextFont(m_hSmallTextFont); + surface()->DrawSetTextColor(textColorTransparent); + int weaponNamePixelWidth, weaponNamePixelHeight; + surface()->GetTextSize(m_hSmallTextFont, unicodeWepName, weaponNamePixelWidth, weaponNamePixelHeight); + surface()->DrawSetTextPos(xPos1 - ((margin * 2) + weaponNamePixelWidth + m_fontWidth / 2), yPos0 + (margin / 2)); surface()->DrawPrintText(unicodeWepName, textLen); + if(dynamic_cast (activeWep)) + { + return; + } + const int maxClip = activeWep->GetMaxClip1(); - if (maxClip != 0) + if (maxClip != 0 && !activeWep->IsMeleeWeapon()) { const auto ammo = GetAmmoDef()->GetAmmoOfIndex(activeWep->GetPrimaryAmmoType()); const int ammoCount = activeWep->GetOwner()->GetAmmoCount(ammo->pName); - const int numClips = abs(ammoCount / activeWep->GetMaxClip1()); // abs because grenades return negative values (???) + const int numClips = ceil(abs((float)ammoCount / activeWep->GetMaxClip1())); // abs because grenades return negative values (???) // casting division to float in case we have a half-empty mag, rounding up to show the half mag as one more mag + const auto isSupa = dynamic_cast(activeWep); + + const int maxLen = 5; + char clipsText[maxLen]{ '\0' }; + if(isSupa) + { + const auto secondaryAmmo = GetAmmoDef()->GetAmmoOfIndex(activeWep->GetSecondaryAmmoType()); + snprintf(clipsText, 10, "%d+%d", ammoCount, activeWep->GetOwner()->GetAmmoCount(secondaryAmmo->pName)); + } else + { + snprintf(clipsText, 10, "%d", numClips); + } + textLen = V_strlen(clipsText); + wchar_t unicodeClipsText[maxLen]{ L'\0' }; + g_pVGuiLocalize->ConvertANSIToUnicode(clipsText, unicodeClipsText, sizeof(unicodeClipsText)); + + int clipsTextWidth, clipsTextHeight; + surface()->GetTextSize(m_hTextFont, unicodeClipsText, clipsTextWidth, clipsTextHeight); + surface()->DrawSetTextFont(m_hTextFont); + surface()->DrawSetTextPos(xPos1 - (clipsTextWidth + margin), yPos0 + (margin * 2) + m_smallFontHeight); + surface()->DrawPrintText(unicodeClipsText, textLen); + + const auto neoWep = dynamic_cast (activeWep); - // Render amount of clips remaining. Sort of an approximation, should revisit once unfinished mag "reload dumping" is implemented. - if (numClips != 0) + char* ammoChar = nullptr; + int fireModeWidth = 0, fireModeHeight = 0; + int magSizeMax = 0; + int magSizeCurrent = 0; + + if (activeWep->UsesClipsForAmmo1()) + { + char fireModeText[2]{ L'\0' }; + + ammoChar = const_cast(activeWep->GetWpnData().szBulletCharacter); + magSizeMax = activeWep->GetMaxClip1(); + magSizeCurrent = activeWep->Clip1(); + + if(neoWep) + { + if(neoWep->IsAutomatic()) + fireModeText[0] = 'j'; + else if(isSupa) + if(isSupa->SlugLoaded()) + fireModeText[0] = 'h'; + else + fireModeText[0] = 'l'; + else + fireModeText[0] = 'h'; + + + wchar_t unicodeFireModeText[2]{ L'\0' }; + g_pVGuiLocalize->ConvertANSIToUnicode(fireModeText, unicodeFireModeText, sizeof(unicodeFireModeText)); + + surface()->DrawSetTextFont(m_hBulletFont); + surface()->DrawSetTextPos(xPos0 + margin, yPos0 + ((((yPos1 - margin) - yPos0) / 2) - ((m_bulletFontHeight * 0.8) / 2))); + surface()->DrawPrintText(unicodeFireModeText, V_strlen(fireModeText)); + + surface()->GetTextSize(m_hBulletFont, unicodeFireModeText, fireModeWidth, fireModeHeight); + } + } else + { + if(dynamic_cast (activeWep)) + { + ammoChar = new char[2] { 'f', '\0' }; + magSizeMax = magSizeCurrent = ammoCount; + } else if(dynamic_cast (activeWep)) + { + ammoChar = new char[2] { 'g', '\0' }; + magSizeMax = magSizeCurrent = ammoCount; + } + } + + auto maxSpaceAvaliableForBullets = (xPos1 - (margin + max(clipsTextWidth, m_fontWidth))) - (xPos0 + fireModeWidth + (margin * 2)); + auto bulletWidth = surface()->GetCharacterWidth(m_hBulletFont, *ammoChar); + auto plusWidth = surface()->GetCharacterWidth(m_hBulletFont, '+'); + auto maxBulletsWeCanDisplay = maxSpaceAvaliableForBullets / bulletWidth; + auto maxBulletsWeCanDisplayWithPlus = (maxSpaceAvaliableForBullets - plusWidth) / bulletWidth; + auto bulletsOverflowing = maxBulletsWeCanDisplay < magSizeMax; + + if(bulletsOverflowing) { - const int maxLen = 4; // support a max of '999' clips, plus '\0' - char clipsText[maxLen]{ '\0' }; - itoa(numClips, clipsText, 10); - textLen = V_strlen(clipsText); - wchar_t unicodeClipsText[maxLen]{ L'\0' }; - g_pVGuiLocalize->ConvertANSIToUnicode(clipsText, unicodeClipsText, sizeof(unicodeClipsText)); - - surface()->DrawSetTextPos(m_resX - fontWidth * 1.5 - margin, ypos + fontHeight * 2.5 - margin); - surface()->DrawPrintText(unicodeClipsText, textLen); + magSizeMax = maxBulletsWeCanDisplayWithPlus + 1; } - // Render the bullet icons representing the amount of bullets in current clip. - if (ammoCount != 0 && activeWep->UsesClipsForAmmo1()) + magSizeMax = min(magSizeMax, 64); + char bullets[64]{ '\0' }; + for(int i = 0; i < magSizeMax; i++) { - const int maxBulletsInClip = 63 + 1; - char bullets[maxBulletsInClip]{ '\0' }; - for (int i = 0, numBulletsInCurClip = activeWep->Clip1(); i < maxBulletsInClip && numBulletsInCurClip != 0; ++i) { - V_strcat_safe(bullets, "a"); - --numBulletsInCurClip; + bullets[i] = *ammoChar; + } + + int magAmountToDrawFilled = magSizeCurrent; + + if(bulletsOverflowing) + { + bullets[magSizeMax - 1] = '+'; + + if(maxClip == magSizeCurrent) + { + magAmountToDrawFilled = magSizeMax; + } else if(magSizeMax - 1 < magSizeCurrent) + { + magAmountToDrawFilled = magSizeMax - 1; + } else + { + magAmountToDrawFilled = magSizeCurrent; } - wchar_t unicodeBullets[maxBulletsInClip]{ L'\0' }; - g_pVGuiLocalize->ConvertANSIToUnicode(bullets, unicodeBullets, sizeof(unicodeBullets)); + } + + wchar_t unicodeBullets[64]; + g_pVGuiLocalize->ConvertANSIToUnicode(bullets, unicodeBullets, sizeof(unicodeBullets)); + + surface()->DrawSetTextFont(m_hBulletFont); + surface()->DrawSetTextPos(xPos0 + fireModeWidth + (margin * 2), (yPos0 + margin + m_smallFontHeight) - (m_bulletFontHeight * 0.2)); + surface()->DrawPrintText(unicodeBullets, magAmountToDrawFilled); - surface()->DrawSetTextFont(m_hBulletFont); - surface()->DrawSetTextPos(xpos + 10 - margin, ypos + 10 - margin); // TODO: resolution scaling for the offsets here - surface()->DrawPrintText(unicodeBullets, activeWep->Clip1()); + if(maxClip > 0) + { + surface()->DrawSetColor(textColor); + surface()->DrawSetTextPos(xPos0 + fireModeWidth + (margin * 2), (yPos0 + margin + m_smallFontHeight) - (m_bulletFontHeight * 0.2)); + surface()->DrawPrintText(unicodeBullets, magSizeMax); } } } diff --git a/mp/src/game/client/neo/ui/neo_hud_ammo.h b/mp/src/game/client/neo/ui/neo_hud_ammo.h index 518498e47..4c5050b1f 100644 --- a/mp/src/game/client/neo/ui/neo_hud_ammo.h +++ b/mp/src/game/client/neo/ui/neo_hud_ammo.h @@ -31,10 +31,14 @@ class CNEOHud_Ammo : public CNEOHud_ChildElement, public CHudElement, public vgu void DrawAmmo() const; private: + vgui::HFont m_hSmallTextFont; vgui::HFont m_hTextFont; vgui::HFont m_hBulletFont; int m_resX, m_resY; + int m_smallFontWidth, m_smallFontHeight; + int m_fontWidth; + int m_bulletFontWidth, m_bulletFontHeight; private: CNEOHud_Ammo(const CNEOHud_Ammo& other); diff --git a/mp/src/game/client/neo/ui/neo_hud_childelement.cpp b/mp/src/game/client/neo/ui/neo_hud_childelement.cpp index 699a15558..368d57149 100644 --- a/mp/src/game/client/neo/ui/neo_hud_childelement.cpp +++ b/mp/src/game/client/neo/ui/neo_hud_childelement.cpp @@ -11,7 +11,7 @@ using vgui::surface; -#define NEO_HUDBOX_COLOR Color(116, 116, 116, 200) +#define NEO_HUDBOX_COLOR Color(116, 116, 116, 178) #define NEO_HUDBOX_CORNER_SCALE 1.0 // NEO TODO (Rain): this should be expanded into two margin_width/height cvars, so players can tweak their HUD position if they wish to. @@ -75,3 +75,9 @@ void CNEOHud_ChildElement::DrawNeoHudRoundedBox(const int x0, const int y0, cons surface()->DrawFilledRect(x0, y0h, x0w, y1h); surface()->DrawFilledRect(x1w, y0h, x1, y1h); } + +int CNEOHud_ChildElement::GetMargin() +{ + return neo_cl_hud_margin.GetInt(); +} + diff --git a/mp/src/game/client/neo/ui/neo_hud_childelement.h b/mp/src/game/client/neo/ui/neo_hud_childelement.h index cc94ac73b..d8ce7a6e9 100644 --- a/mp/src/game/client/neo/ui/neo_hud_childelement.h +++ b/mp/src/game/client/neo/ui/neo_hud_childelement.h @@ -78,6 +78,8 @@ class CNEOHud_ChildElement CNeoHudElements* GetRootNeoHud() const { return m_pNeoHud; } + static int GetMargin(); + private: float GetUpdateFrequency() const { return GetUpdateFrequencyConVar()->GetFloat(); } diff --git a/mp/src/game/shared/neo/weapons/weapon_supa7.cpp b/mp/src/game/shared/neo/weapons/weapon_supa7.cpp index 97e522d31..0266f8f5f 100644 --- a/mp/src/game/shared/neo/weapons/weapon_supa7.cpp +++ b/mp/src/game/shared/neo/weapons/weapon_supa7.cpp @@ -530,3 +530,15 @@ void CWeaponSupa7::AddViewKick(void) punch.Init(SharedRandomFloat("supapax", -2, -1), SharedRandomFloat("supapay", -1, 1), 0); pPlayer->ViewPunch(punch); } + +bool C_WeaponSupa7::SlugLoaded() const +{ + return m_bSlugLoaded; +} + + +bool C_WeaponSupa7::SlugLoaded() const +{ + return m_bSlugLoaded; +} + diff --git a/mp/src/game/shared/neo/weapons/weapon_supa7.h b/mp/src/game/shared/neo/weapons/weapon_supa7.h index 9d4afcea8..554ad1212 100644 --- a/mp/src/game/shared/neo/weapons/weapon_supa7.h +++ b/mp/src/game/shared/neo/weapons/weapon_supa7.h @@ -46,6 +46,7 @@ class CWeaponSupa7 : public CNEOBaseCombatWeapon bool StartReloadSlug(void); bool Reload(void); bool ReloadSlug(void); + bool SlugLoaded(void) const; void FillClip(void); void FillClipSlug(void);