diff --git a/src/game/client/CMakeLists.txt b/src/game/client/CMakeLists.txt index 317cbb87a..7cf122623 100644 --- a/src/game/client/CMakeLists.txt +++ b/src/game/client/CMakeLists.txt @@ -1674,6 +1674,7 @@ set(UNITY_SOURCE_NEO_UI neo/ui/neo_scoreboard.cpp neo/ui/neo_hud_context_hint.cpp neo/ui/neo_spec_player_by_hud.cpp + neo/ui/neo_avatar.cpp ) set_source_files_properties( @@ -1715,6 +1716,7 @@ target_sources_grouped( neo/ui/neo_hud_worldpos_marker_generic.h neo/ui/neo_scoreboard.h neo/ui/neo_hud_context_hint.h + neo/ui/neo_avatar.h ${UNITY_SOURCE_NEO_UI} ) diff --git a/src/game/client/neo/ui/neo_avatar.cpp b/src/game/client/neo/ui/neo_avatar.cpp new file mode 100644 index 000000000..2d0defc6b --- /dev/null +++ b/src/game/client/neo/ui/neo_avatar.cpp @@ -0,0 +1,170 @@ +#include "neo_avatar.h" + +#include "cbase.h" +#include +#include +#include "VGuiMatSurface/IMatSystemSurface.h" + +struct CacheAvatarValue +{ + int normal; + int dead; +}; + +struct CacheAvatarKey +{ + CSteamID m_SteamID; + int m_iAvatar = 0; + + bool operator<(const CacheAvatarKey &rhs) const + { + return m_SteamID.ConvertToUint64() < rhs.m_SteamID.ConvertToUint64() + || (m_SteamID.ConvertToUint64() == rhs.m_SteamID.ConvertToUint64() + && m_iAvatar < rhs.m_iAvatar); + } +}; + +static inline CUtlMap gAvatarImageCache; +static inline bool gAvatarImageCacheInit = false; + +void NeoAvatar::SetSteamID(const CSteamID &steamID) +{ + if (steamID == m_SteamID) + { + return; + } + m_SteamID = steamID; + m_iTextureID = 0; + m_iTextureDeadID = 0; + m_flPrevLoadAttempt = 0.0f; +} + +void NeoAvatar::Fetch(const int iAvatarWH) +{ + if (!m_SteamID.IsValid() + || !SteamFriends() + || !SteamUtils() + || (m_iTextureID > 0 && m_iTexForAvatarWH == iAvatarWH) + || (m_flPrevLoadAttempt + 1.0f) > gpGlobals->realtime) + { + return; + } + + if (!SteamFriends()->RequestUserInformation(m_SteamID, false)) + { + int iAvatar = 0; + if (iAvatarWH <= 32) + { + iAvatar = SteamFriends()->GetSmallFriendAvatar(m_SteamID); + } + else if (iAvatarWH <= 64) + { + iAvatar = SteamFriends()->GetMediumFriendAvatar(m_SteamID); + } + else + { + iAvatar = SteamFriends()->GetLargeFriendAvatar(m_SteamID); + } + m_iTexForAvatarWH = iAvatarWH; + + if (iAvatar > 0) + { + if (!gAvatarImageCacheInit) + { + SetDefLessFunc(gAvatarImageCache); + gAvatarImageCacheInit = true; + } + + int iTexIndex = gAvatarImageCache.Find(CacheAvatarKey{m_SteamID, iAvatar}); + if (iTexIndex == gAvatarImageCache.InvalidIndex()) + { + uint32 u32wide = 0, u32tall = 0; + if (SteamUtils()->GetImageSize(iAvatar, &u32wide, &u32tall) + && u32wide > 0 && u32tall > 0) + { + const int wide = u32wide; + const int tall = u32tall; + const int destBufferSize = wide * tall * 4; + byte *rgbaBuf = (byte *)stackalloc(destBufferSize); + if (SteamUtils()->GetImageRGBA(iAvatar, rgbaBuf, destBufferSize)) + { + // Create normal avatar from RGBA without edits + m_iTextureID = vgui::surface()->CreateNewTextureID(true); + g_pMatSystemSurface->DrawSetTextureRGBAEx2(m_iTextureID, rgbaBuf, wide, tall, IMAGE_FORMAT_RGBA8888, true); + + // Create dead avatar from RGBA with redness edits + for (int offset = 0; offset < (wide * tall * 4); offset += 4) + { + constexpr float brightness = 0.5f; + constexpr float contrast = 1.5f; + + float r = (rgbaBuf + offset)[0] / 255.0f; + float g = (rgbaBuf + offset)[1] / 255.0f; + float b = (rgbaBuf + offset)[2] / 255.0f; + + // Contrast + r = Clamp((r - 0.5f) * contrast + 0.5f, 0.0f, 1.0f); + g = Clamp((g - 0.5f) * contrast + 0.5f, 0.0f, 1.0f); + b = Clamp((b - 0.5f) * contrast + 0.5f, 0.0f, 1.0f); + + // Convert to grayscale - Luminosity, then gradient from black -> red -> white + const float gray = 0.3f * r + 0.59f * g + 0.11f * b; + if (gray < 0.5) + { + r = gray * 2.0f; + g = 0; + b = 0; + } + else { + r = 1.0f; + g = (gray - 0.5f) * 2.0f; + b = (gray - 0.5f) * 2.0f; + } + + // Brightness + r = Clamp(r * brightness, 0.0f, 1.0f); + g = Clamp(g * brightness, 0.0f, 1.0f); + b = Clamp(b * brightness, 0.0f, 1.0f); + + (rgbaBuf + offset)[0] = r * 255; + (rgbaBuf + offset)[1] = g * 255; + (rgbaBuf + offset)[2] = b * 255; + } + + m_iTextureDeadID = vgui::surface()->CreateNewTextureID(true); + g_pMatSystemSurface->DrawSetTextureRGBAEx2(m_iTextureDeadID, rgbaBuf, wide, tall, IMAGE_FORMAT_RGBA8888, true); + + // Add textures to global cache + iTexIndex = gAvatarImageCache.Insert(CacheAvatarKey{m_SteamID, iAvatar}); + gAvatarImageCache[iTexIndex].normal = m_iTextureID; + gAvatarImageCache[iTexIndex].dead = m_iTextureDeadID; + } + stackfree(rgbaBuf); + } + } + else + { + m_iTextureID = gAvatarImageCache[iTexIndex].normal; + m_iTextureDeadID = gAvatarImageCache[iTexIndex].dead; + } + } + } + + if (m_iTextureID == 0) + { + m_flPrevLoadAttempt = gpGlobals->realtime; + } +} + +void NeoAvatar::Paint(const int x, const int y, const int widetall, + const bool bDead) const +{ + if (m_iTextureID == 0) + { + return; + } + vgui::surface()->DrawSetTexture(bDead ? m_iTextureDeadID : m_iTextureID); + vgui::surface()->DrawSetColor(COLOR_WHITE); + vgui::surface()->DrawTexturedRect(x, y, x + widetall, y + widetall); +} + diff --git a/src/game/client/neo/ui/neo_avatar.h b/src/game/client/neo/ui/neo_avatar.h new file mode 100644 index 000000000..b9fe6f94e --- /dev/null +++ b/src/game/client/neo/ui/neo_avatar.h @@ -0,0 +1,18 @@ +#pragma once + +#include "steam/steam_api.h" + +struct NeoAvatar +{ + CSteamID m_SteamID; + int m_iTextureID = 0; + int m_iTextureDeadID = 0; + int m_iTexForAvatarWH = 0; + float m_flPrevLoadAttempt = 0.0f; + + void SetSteamID(const CSteamID &steamID); + void Fetch(const int iAvatarWH); + void Paint(const int x, const int y, const int widetall, + const bool bDead = false) const; +}; + diff --git a/src/game/client/neo/ui/neo_hud_killer_info.cpp b/src/game/client/neo/ui/neo_hud_killer_info.cpp index 81d153385..e10d57c64 100644 --- a/src/game/client/neo/ui/neo_hud_killer_info.cpp +++ b/src/game/client/neo/ui/neo_hud_killer_info.cpp @@ -37,10 +37,6 @@ CNEOHud_KillerInfo::CNEOHud_KillerInfo(const char *pszName, vgui::Panel *parent) } SetVisible(false); CommonSetupHUD(); - - m_avatar32.SetAvatarSize(32, 32); - m_avatar64.SetAvatarSize(64, 64); - m_avatar184.SetAvatarSize(184, 184); } void CNEOHud_KillerInfo::ApplySchemeSettings(vgui::IScheme *pScheme) @@ -77,18 +73,10 @@ void CNEOHud_KillerInfo::UpdateStateForNeoHudElementDraw() && (gpGlobals->curtime < (const_cast(pLocalPlayer)->GetDeathTime() + DEATH_ANIMATION_TIME)); const CSteamID steamID = GetSteamIDForPlayerIndex(g_neoKillerInfos.iEntIndex); - if (steamID.IsValid()) - { - m_avatar32.SetAvatarSteamID(steamID, k_EAvatarSize32x32); - m_avatar64.SetAvatarSteamID(steamID, k_EAvatarSize64x64); - m_avatar184.SetAvatarSteamID(steamID, k_EAvatarSize184x184); - } - else - { - m_avatar32.ClearAvatarSteamID(); - m_avatar64.ClearAvatarSteamID(); - m_avatar184.ClearAvatarSteamID(); - } + m_avatar.SetSteamID( + (steamID.IsValid() && (CNEOScoreBoard::ShowAvatars())) + ? steamID : CSteamID{}); + m_avatar.Fetch(184); } void CNEOHud_KillerInfo::DrawNeoHudElement() @@ -116,28 +104,12 @@ void CNEOHud_KillerInfo::DrawNeoHudElement() const bool bSuicideKill = (iKillerEntIndex == pLocalPlayer->entindex()) || (iKillerEntIndex == NEO_ENVIRON_KILLED); - - CAvatarImage *pAvatarImg = nullptr; - if (CNEOScoreBoard::ShowAvatars()) - { - if (m_avatar184.IsValid()) - { - pAvatarImg = &m_avatar184; - } - else if (m_avatar64.IsValid()) - { - pAvatarImg = &m_avatar64; - } - else if (m_avatar32.IsValid()) - { - pAvatarImg = &m_avatar32; - } - } + const bool bValidAvatar = m_avatar.m_iTextureID > 0; // NEO NOTE (nullsystem): Similar sizing to how the scoreboard popup card is const int iMargin = wide / 384; - const int iAvatarWT = pAvatarImg ? (((tall / 35) * 3) - (iMargin * 2)) : 0; - const int iTextOffsetX = pAvatarImg ? (iAvatarWT + (2 * iMargin)) : iMargin; + const int iAvatarWT = bValidAvatar ? (((tall / 35) * 3) - (iMargin * 2)) : 0; + const int iTextOffsetX = bValidAvatar ? (iAvatarWT + (2 * iMargin)) : iMargin; wchar_t wszText[256] = {}; @@ -161,7 +133,7 @@ void CNEOHud_KillerInfo::DrawNeoHudElement() vgui::surface()->GetTextSize(m_hFontNormal, L"A", iNormalWide, iNormalTall); const int iBoxWide = Max(wide / 4, iTextOffsetX + iTitleWide + iMargin); - const int iBoxHeight = (pAvatarImg) + const int iBoxHeight = (bValidAvatar) ? (2 * iMargin) + iAvatarWT : iMargin + iTitleTall + iNormalTall + iMargin; @@ -173,12 +145,9 @@ void CNEOHud_KillerInfo::DrawNeoHudElement() }; DrawNeoHudRoundedBox(rect.x0, rect.y0, rect.x1, rect.y1, COLOR_BLACK_TRANSPARENT); - if (pAvatarImg) + if (bValidAvatar) { - pAvatarImg->m_bDeadAvatar = false; - pAvatarImg->SetPos(rect.x0 + iMargin, rect.y0 + iMargin); - pAvatarImg->SetSize(iAvatarWT, iAvatarWT); - pAvatarImg->Paint(); + m_avatar.Paint(rect.x0 + iMargin, rect.y0 + iMargin, iAvatarWT); } vgui::surface()->DrawSetTextFont(m_hFontTitle); diff --git a/src/game/client/neo/ui/neo_hud_killer_info.h b/src/game/client/neo/ui/neo_hud_killer_info.h index b213d430d..bd630fcf0 100644 --- a/src/game/client/neo/ui/neo_hud_killer_info.h +++ b/src/game/client/neo/ui/neo_hud_killer_info.h @@ -5,7 +5,7 @@ #include #include -#include +#include "neo_avatar.h" class CNEOHud_KillerInfo : public CNEOHud_ChildElement, public CHudElement, public vgui::Panel { @@ -28,8 +28,6 @@ class CNEOHud_KillerInfo : public CNEOHud_ChildElement, public CHudElement, publ vgui::HFont m_hFontNormal; vgui::HFont m_hFontTitle; - CAvatarImage m_avatar32; - CAvatarImage m_avatar64; - CAvatarImage m_avatar184; + NeoAvatar m_avatar = {}; bool m_bPlayerShownHud = false; }; diff --git a/src/game/client/neo/ui/neo_hud_round_state.cpp b/src/game/client/neo/ui/neo_hud_round_state.cpp index 6da8aafd7..173c9fea7 100644 --- a/src/game/client/neo/ui/neo_hud_round_state.cpp +++ b/src/game/client/neo/ui/neo_hud_round_state.cpp @@ -18,7 +18,6 @@ #include "c_neo_player.h" #include "c_team.h" #include "c_playerresource.h" -#include "vgui_avatarimage.h" #include "neo_scoreboard.h" #include "neo_hud_spectator_overlay.h" diff --git a/src/game/client/neo/ui/neo_hud_spectator_overlay.cpp b/src/game/client/neo/ui/neo_hud_spectator_overlay.cpp index 702a60a92..471c89278 100644 --- a/src/game/client/neo/ui/neo_hud_spectator_overlay.cpp +++ b/src/game/client/neo/ui/neo_hud_spectator_overlay.cpp @@ -20,9 +20,7 @@ #include #include #include -#include #include -#include // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -163,7 +161,6 @@ void CNEOHud_SpectatorOverlay::LevelShutdown() { ClearAll(); } - void CNEOHud_SpectatorOverlay::FireGameEvent(IGameEvent *event) { const char *type = event->GetName(); @@ -217,12 +214,13 @@ void CNEOHud_SpectatorOverlay::ApplySchemeSettings(vgui::IScheme* pScheme) SetFgColor(COLOR_TRANSPARENT); SetBgColor(COLOR_TRANSPARENT); + + m_iCardsSize = 0; + V_memset(m_cards, 0, sizeof(m_cards)); } void CNEOHud_SpectatorOverlay::UpdateStateForNeoHudElementDraw() { - bool bScoreboardNeedUpdate = false; - const int iOldCardsSize = m_iCardsSize; SpectatorPlayerCard oldCards[MAX_PLAYERS_ARRAY_SAFE] = {}; static_assert(sizeof(oldCards) == sizeof(m_cards)); @@ -238,6 +236,14 @@ void CNEOHud_SpectatorOverlay::UpdateStateForNeoHudElementDraw() return; } + int iScrWide, iScrTall; + vgui::surface()->GetScreenSize(iScrWide, iScrTall); + + const float flWide = static_cast(iScrWide); + float flWideAs43 = static_cast(iScrTall) * (4.0f / 3.0f); + if (flWideAs43 > flWide) flWideAs43 = flWide; + const int iAvatarSize = flWideAs43 / 22.5f; + for (const auto iPutTeam : {TEAM_JINRAI, TEAM_NSF}) { for (int i = 1; i <= gpGlobals->maxClients; ++i) @@ -253,38 +259,16 @@ void CNEOHud_SpectatorOverlay::UpdateStateForNeoHudElementDraw() continue; } - SpectatorPlayerCard *pCard = &m_cards[m_iCardsSize++]; + NeoAvatar *pAvatar = &m_avatars[m_iCardsSize]; + SpectatorPlayerCard *pCard = &m_cards[m_iCardsSize]; + ++m_iCardsSize; ++m_iTeamPlayersCount[iTeam]; - // Reuse the avatar image already generated by the scoreboard - pCard->pAvatar = nullptr; - if (g_pNeoScoreBoard && g_pNeoScoreBoard->m_pImageList && CNEOScoreBoard::ShowAvatars()) - { - const CSteamID steamId = GetSteamIDForPlayerIndex(i); - if (steamId.IsValid()) - { - const CUtlMap &mapAvatars = g_pNeoScoreBoard->m_mapAvatarsToImageList; - const int iMapIdx = mapAvatars.Find(steamId); - bool bAvatarNeedUpdate = true; - if (iMapIdx != mapAvatars.InvalidIndex()) - { - const MapAvatarValue &avatar = mapAvatars[iMapIdx]; - if (avatar.i184Idx >= 0) - { - pCard->pAvatar = static_cast(g_pNeoScoreBoard->m_pImageList->GetImage(avatar.i184Idx)); - bAvatarNeedUpdate = (!pCard->pAvatar || !pCard->pAvatar->IsValid()); - } if ((!pCard->pAvatar || !pCard->pAvatar->IsValid()) && avatar.i64Idx >= 0) - { - pCard->pAvatar = static_cast(g_pNeoScoreBoard->m_pImageList->GetImage(avatar.i64Idx)); - } - if ((!pCard->pAvatar || !pCard->pAvatar->IsValid()) && avatar.i32Idx >= 0) - { - pCard->pAvatar = static_cast(g_pNeoScoreBoard->m_pImageList->GetImage(avatar.i32Idx)); - } - } - bScoreboardNeedUpdate = bScoreboardNeedUpdate || bAvatarNeedUpdate; - } - } + const CSteamID steamID = GetSteamIDForPlayerIndex(i); + pAvatar->SetSteamID( + (steamID.IsValid() && CNEOScoreBoard::ShowAvatars()) + ? steamID : CSteamID{}); + pAvatar->Fetch(iAvatarSize); Q_UTF8ToUnicode(g_PR->GetPlayerName(i), pCard->wszPlayerName, sizeof(pCard->wszPlayerName)); @@ -392,25 +376,17 @@ void CNEOHud_SpectatorOverlay::UpdateStateForNeoHudElementDraw() } } } - - // NEO NOTE (nullsystem): This is so the spectator don't need to tab press - // on the scoreboard to update the avatar. Generally only happens in the - // beginning of match/demo - // - // NEO TODO (nullsystem): Move avatar handling off to something separate from - // the scoreboard but shared between scoreboard, overlay, and killer info - if (bScoreboardNeedUpdate) - { - g_pNeoScoreBoard->Update(); - } } -void CNEOHud_SpectatorOverlay::DrawPlayerCard(const SpectatorPlayerCard &card, +void CNEOHud_SpectatorOverlay::DrawPlayerCard(const int iPlayerIdx, const bool bIsLeftSide, const int x, const int y, const int wide, const int tall, const Color accentColor, const float flWideAs43) const { + const SpectatorPlayerCard &card = m_cards[iPlayerIdx]; + const NeoAvatar &avatar = m_avatars[iPlayerIdx]; + // Card background static constexpr const float FL_LAST_ALIVE_DELTA_MAX = 1.5f; const float flLastAliveDelta = gpGlobals->curtime - card.flLastAliveTime; @@ -431,13 +407,11 @@ void CNEOHud_SpectatorOverlay::DrawPlayerCard(const SpectatorPlayerCard &card, const int iAvatarX = bIsLeftSide ? x : x + wide - iAvatarSize; const int iAvatarY = y; - // Draw the actual Steam avatar (borrowed from the scoreboard's avatar list) - if (card.pAvatar && card.pAvatar->IsValid()) + // Draw the actual Steam avatar + if (avatar.m_iTextureID > 0) { - card.pAvatar->m_bDeadAvatar = !card.bAlive; - card.pAvatar->SetPos(iAvatarX, iAvatarY); - card.pAvatar->SetSize(iAvatarSize, iAvatarSize); - card.pAvatar->Paint(); + avatar.Paint(iAvatarX, iAvatarY, + iAvatarSize, !card.bAlive); } else if (card.wszPlayerName[0]) { @@ -1032,7 +1006,7 @@ void CNEOHud_SpectatorOverlay::DrawNeoHudElement() { const int iRowY = iColYStart + (iPlayerRow * iAvatarWH) + ((iPlayerRow > 0) ? (iPlayerRow * iGapBetween) : 0); - DrawPlayerCard(m_cards[i], bIsLeftSide, + DrawPlayerCard(i, bIsLeftSide, iColXStart, iRowY, iTotalCardWide, iAvatarWH, accentColor, flWideAs43); ++iPlayerRow; diff --git a/src/game/client/neo/ui/neo_hud_spectator_overlay.h b/src/game/client/neo/ui/neo_hud_spectator_overlay.h index 6fc9d7c89..802e4cb58 100644 --- a/src/game/client/neo/ui/neo_hud_spectator_overlay.h +++ b/src/game/client/neo/ui/neo_hud_spectator_overlay.h @@ -3,8 +3,8 @@ #include "neo_hud_childelement.h" #include "hudelement.h" #include - -class CAvatarImage; +#include +#include "neo_avatar.h" extern ConVar cl_neo_hud_spectator_overlay_enabled; extern ConVar cl_neo_hud_spectator_overlay_jinrai_name; @@ -34,7 +34,6 @@ struct SpectatorPlayerCard int iHP = 0; int iRoundKills = 0; bool bAlive = false; - CAvatarImage *pAvatar = nullptr; // Used for animations/fading float flLastAttackTime = 0.0f; float flLastAliveTime = 0.0f; @@ -61,6 +60,8 @@ class CNEOHud_SpectatorOverlay : public CNEOHud_ChildElement, public CHudElement void Paint() final; SpectatorPlayerCard m_cards[MAX_PLAYERS_ARRAY_SAFE] = {}; + // m_avatars separate as m_cards always cleared + NeoAvatar m_avatars[MAX_PLAYERS_ARRAY_SAFE] = {}; int m_iCardsSize = 0; enum ESpecType @@ -86,7 +87,7 @@ class CNEOHud_SpectatorOverlay : public CNEOHud_ChildElement, public CHudElement ConVar *GetUpdateFrequencyConVar() const final; private: - void DrawPlayerCard(const SpectatorPlayerCard &card, + void DrawPlayerCard(const int iPlayerIdx, const bool bIsLeftSide, const int x, const int y, const int wide, const int tall, diff --git a/src/game/client/neo/ui/neo_scoreboard.cpp b/src/game/client/neo/ui/neo_scoreboard.cpp index 9d7147d13..87fe71d38 100644 --- a/src/game/client/neo/ui/neo_scoreboard.cpp +++ b/src/game/client/neo/ui/neo_scoreboard.cpp @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include @@ -73,9 +72,6 @@ CNEOScoreBoard::CNEOScoreBoard(IViewPort *pViewPort) ListenForGameEvent("game_newmap"); ListenForGameEvent("hltv_status"); - m_mapAvatarsToImageList.SetLessFunc(DefLessFunc(CSteamID)); - m_mapAvatarsToImageList.RemoveAll(); - g_pNeoScoreBoard = this; for (int i = 0; i < CROSSHAIR_STYLE__TOTAL; ++i) @@ -96,10 +92,6 @@ CNEOScoreBoard::~CNEOScoreBoard() { g_pNeoScoreBoard = nullptr; } - if (m_pImageList) - { - delete m_pImageList; - } } bool CNEOScoreBoard::ShowAvatars() @@ -153,7 +145,7 @@ void CNEOScoreBoard::ShowPanel(bool bShow) } // Catch the case where we call ShowPanel before ApplySchemeSettings, eg when // going from windowed <-> fullscreen - if (m_pImageList == NULL) + if (!m_bAppliedApplyScheme) { InvalidateLayout(true, true); } @@ -229,12 +221,7 @@ void CNEOScoreBoard::ApplySchemeSettings(vgui::IScheme *pScheme) m_playerPopup = {}; m_iTotalPlayers = 0; - if (m_pImageList) - { - delete m_pImageList; - } - m_pImageList = new vgui::ImageList(false); - m_mapAvatarsToImageList.RemoveAll(); + m_bAppliedApplyScheme = true; m_flNextUpdateTime = gpGlobals->curtime + 0.1f; @@ -298,7 +285,6 @@ void CNEOScoreBoard::Reset() m_iTotalPlayers = 0; V_memset(m_playersInfo, 0, sizeof(m_playersInfo)); m_flNextUpdateTime = 0; - m_mapAvatarsToImageList.RemoveAll(); } void CNEOScoreBoard::ToggleMouseCapture(const bool bUseMouse) @@ -491,44 +477,12 @@ void CNEOScoreBoard::Update() } } - // pPlayerInfo->avatar - if (ShowAvatars() && pPlayerInfo->steamID.IsValid()) - { - // See if we already have that avatar in our list - int iMapIndex = m_mapAvatarsToImageList.Find(pPlayerInfo->steamID); - if (iMapIndex == m_mapAvatarsToImageList.InvalidIndex()) - { - auto *pImage32 = new CAvatarImage; - auto *pImage64 = new CAvatarImage; - auto *pImage184 = new CAvatarImage; - - pImage32->SetAvatarSize(32, 32); - pImage64->SetAvatarSize(64, 64); - pImage184->SetAvatarSize(184, 184); + pPlayerInfo->iAvatarIdx = m_iTotalPlayers; - pImage32->SetAvatarSteamID(pPlayerInfo->steamID, k_EAvatarSize32x32); - pImage64->SetAvatarSteamID(pPlayerInfo->steamID, k_EAvatarSize64x64); - pImage184->SetAvatarSteamID(pPlayerInfo->steamID, k_EAvatarSize184x184); - - pPlayerInfo->avatar = { - .i32Idx = m_pImageList->AddImage(pImage32), - .i64Idx = m_pImageList->AddImage(pImage64), - .i184Idx = m_pImageList->AddImage(pImage184), - }; - - m_mapAvatarsToImageList.Insert(pPlayerInfo->steamID, pPlayerInfo->avatar); - } - else - { - pPlayerInfo->avatar = m_mapAvatarsToImageList[iMapIndex]; - } - } - else - { - pPlayerInfo->avatar.i32Idx = -1; - pPlayerInfo->avatar.i64Idx = -1; - pPlayerInfo->avatar.i184Idx = -1; - } + NeoAvatar *pAvatar = &m_avatars[m_iTotalPlayers]; + pAvatar->SetSteamID((ShowAvatars() && pPlayerInfo->steamID.IsValid()) + ? pPlayerInfo->steamID : CSteamID{}); + pAvatar->Fetch(64); ++m_iTotalPlayers; } @@ -892,6 +846,7 @@ void CNEOScoreBoard::OnMainLoop(const NeoUI::Mode eMode) for (int i = 0; i < m_iTotalPlayers; ++i) { const CNEOScoreBoardPlayer *pPlayerInfo = &m_playersInfo[i]; + const NeoAvatar *pAvatar = &m_avatars[pPlayerInfo->iAvatarIdx]; const bool bIsPlaying = (pPlayerInfo->iTeam >= FIRST_GAME_TEAM); const bool bIsDMPlaying = false == bIsTeamplay && bIsPlaying; const bool bIsMuted = pPlayerInfo->bMuted && false == pPlayerInfo->bBot; @@ -909,6 +864,8 @@ void CNEOScoreBoard::OnMainLoop(const NeoUI::Mode eMode) && false == pPlayerInfo->bBot) { m_playerPopup = *pPlayerInfo; + m_avatarPopup = *pAvatar; + m_avatarPopup.Fetch(184); const bool bHaveFriendReq = (SteamFriends() && k_EFriendRelationshipRequestInitiator == SteamFriends()->GetFriendRelationship(m_playerPopup.steamID)); @@ -975,32 +932,15 @@ void CNEOScoreBoard::OnMainLoop(const NeoUI::Mode eMode) // Avatar/Dead-indicator NeoUI::Pad(); - CAvatarImage *pAvatarImg = nullptr; - if (ShowAvatars() && pPlayerInfo->avatar.i32Idx >= 0) + const bool bValidAvatar = (pAvatar->m_iTextureID > 0); + if (bValidAvatar) { - // Use higher px image if wanted, otherwise fallback to i32Idx - if (pPlayerInfo->avatar.i64Idx >= 0 && IN_BETWEEN_EQ(32, m_uiCtx.irWidgetTall, 64)) - { - pAvatarImg = (CAvatarImage *)(m_pImageList->GetImage(pPlayerInfo->avatar.i64Idx)); - } - else if (pPlayerInfo->avatar.i184Idx >= 0 && m_uiCtx.irWidgetTall > 64) - { - pAvatarImg = (CAvatarImage *)(m_pImageList->GetImage(pPlayerInfo->avatar.i184Idx)); - } - else - { - pAvatarImg = (CAvatarImage *)(m_pImageList->GetImage(pPlayerInfo->avatar.i32Idx)); - } - - if (pAvatarImg) - { - pAvatarImg->m_bDeadAvatar = pPlayerInfo->bDead && bIsPlaying; - pAvatarImg->SetPos(m_uiCtx.rWidgetArea.x0, m_uiCtx.rWidgetArea.y0); - pAvatarImg->SetSize(m_uiCtx.irWidgetTall, m_uiCtx.irWidgetTall); - pAvatarImg->Paint(); - } + pAvatar->Paint(m_uiCtx.rWidgetArea.x0, + m_uiCtx.rWidgetArea.y0, + m_uiCtx.irWidgetTall, + pPlayerInfo->bDead && bIsPlaying); } - if (bIsMuted && pAvatarImg) + if (bIsMuted && bValidAvatar) { // Slightly redden the avatar vgui::surface()->DrawSetColor(100, 0, 0, 75); @@ -1013,7 +953,7 @@ void CNEOScoreBoard::OnMainLoop(const NeoUI::Mode eMode) if (iCurTeam >= FIRST_GAME_TEAM) { // Darken the avatar if not ready - if (pAvatarImg + if (bValidAvatar && (bShowReadyUp && false == pPlayerInfo->bReady)) { vgui::surface()->DrawSetColor(0, 0, 0, 200); @@ -1133,29 +1073,12 @@ void CNEOScoreBoard::OnMainLoop(const NeoUI::Mode eMode) if (NeoUI::BeginPopup(NEOSCOREBOARDPOPUP_CARD)) { - CAvatarImage *pAvatarImg = nullptr; - if (ShowAvatars()) - { - if (m_playerPopup.avatar.i184Idx >= 0) - { - pAvatarImg = (CAvatarImage *)(m_pImageList->GetImage(m_playerPopup.avatar.i184Idx)); - } - if ((!pAvatarImg || !pAvatarImg->IsValid()) && m_playerPopup.avatar.i64Idx >= 0) - { - pAvatarImg = (CAvatarImage *)(m_pImageList->GetImage(m_playerPopup.avatar.i64Idx)); - } - if ((!pAvatarImg || !pAvatarImg->IsValid()) && m_playerPopup.avatar.i32Idx >= 0) - { - pAvatarImg = (CAvatarImage *)(m_pImageList->GetImage(m_playerPopup.avatar.i32Idx)); - } - } - if (pAvatarImg) + const bool bValidAvatar = (m_avatarPopup.m_iTextureID > 0); + if (bValidAvatar) { - pAvatarImg->m_bDeadAvatar = false; - pAvatarImg->SetPos(m_uiCtx.dPanel.x + iAvatarOffset, - m_uiCtx.dPanel.y + iAvatarOffset); - pAvatarImg->SetSize(iAvatarWT, iAvatarWT); - pAvatarImg->Paint(); + m_avatarPopup.Paint(m_uiCtx.dPanel.x + iAvatarOffset, + m_uiCtx.dPanel.y + iAvatarOffset, + iAvatarWT); } NeoUI::SetPerRowLayout(1, nullptr, iPopupCardPerRowTallAvatarName); @@ -1163,7 +1086,7 @@ void CNEOScoreBoard::OnMainLoop(const NeoUI::Mode eMode) NeoUI::Pad(); vgui::surface()->DrawSetTextPos( - m_uiCtx.dPanel.x + iAvatarOffset + (pAvatarImg ? (iAvatarWT + iAvatarOffset) : 0), + m_uiCtx.dPanel.x + iAvatarOffset + (bValidAvatar ? (iAvatarWT + iAvatarOffset) : 0), m_uiCtx.dPanel.y + iAvatarOffset); if (m_playerPopup.wszClantag[0]) { @@ -1174,7 +1097,7 @@ void CNEOScoreBoard::OnMainLoop(const NeoUI::Mode eMode) const auto *pFontI = &m_uiCtx.fonts[m_uiCtx.eFont]; const int iClantagTall = vgui::surface()->GetFontTall(pFontI->hdl); vgui::surface()->DrawSetTextPos( - m_uiCtx.dPanel.x + iAvatarOffset + (pAvatarImg ? (iAvatarWT + iAvatarOffset) : 0), + m_uiCtx.dPanel.x + iAvatarOffset + (bValidAvatar ? (iAvatarWT + iAvatarOffset) : 0), m_uiCtx.dPanel.y + iAvatarOffset + iClantagTall + iAvatarOffset); } NeoUI::SwapFont(NeoUI::FONT_NTLARGE); diff --git a/src/game/client/neo/ui/neo_scoreboard.h b/src/game/client/neo/ui/neo_scoreboard.h index 5d96ed318..61ef782ed 100644 --- a/src/game/client/neo/ui/neo_scoreboard.h +++ b/src/game/client/neo/ui/neo_scoreboard.h @@ -8,6 +8,7 @@ #include "neo_player_shared.h" #include "ui/neo_ui.h" #include "neo_crosshair.h" +#include "neo_avatar.h" enum ENeoScoreBoardPadding { @@ -18,13 +19,6 @@ enum ENeoScoreBoardPadding NEOSCOREBOARDPADDING__TOTAL, }; -struct MapAvatarValue -{ - int i32Idx; - int i64Idx; - int i184Idx; -}; - struct CNEOScoreBoardPlayer { // Common section @@ -35,7 +29,9 @@ struct CNEOScoreBoardPlayer bool bBot; bool bMuted; CSteamID steamID; - MapAvatarValue avatar; + // Becauses NeoAvatar is separate as this clears + // often, so have an index to it + int iAvatarIdx; wchar_t wszName[MAX_PLAYER_NAME_LENGTH]; wchar_t wszClantag[NEO_MAX_CLANTAG_LENGTH]; char szCrosshair[NEO_XHAIR_SEQMAX]; @@ -101,13 +97,12 @@ class CNEOScoreBoard : public vgui::Panel, public IViewPortPanel, public CGameEv int m_iTotalPlayers = 0; CNEOScoreBoardPlayer m_playersInfo[MAX_PLAYERS_ARRAY_SAFE] = {}; + NeoAvatar m_avatars[MAX_PLAYERS_ARRAY_SAFE] = {}; CNEOScoreBoardPlayer m_playerPopup = {}; + NeoAvatar m_avatarPopup = {}; wchar_t m_wszHostname[128] = {}; wchar_t m_wszMap[128] = {}; - - vgui::ImageList *m_pImageList = nullptr; - CUtlMap m_mapAvatarsToImageList; float m_flNextUpdateTime = 0.0f; ButtonCode_t m_nCloseKey = BUTTON_CODE_INVALID; @@ -118,6 +113,8 @@ class CNEOScoreBoard : public vgui::Panel, public IViewPortPanel, public CGameEv int iTall; }; Texture m_arTextures[CROSSHAIR_STYLE__TOTAL] = {}; + + bool m_bAppliedApplyScheme = false; }; extern CNEOScoreBoard *g_pNeoScoreBoard;