From f8d660205ebb6fd92f297df0fc87122f11dbfd9b Mon Sep 17 00:00:00 2001 From: nullsystem <15316579+nullsystem@users.noreply.github.com> Date: Fri, 10 Jul 2026 21:47:21 +0100 Subject: [PATCH 1/2] Killer-damage HUD split to scoreboard and killer HUD Damage infos from the killer-damage HUD now comes up in the scoreboard instead. Killer-damage HUD now reduce to just a killer HUD that's a smaller box in the bottom-ish center. Added ability to show avatar also which is taken from the scoreboard's avatars management. Taken/dealt now shows up as columns in the scoreboard when dead. "Deaths" column name renamed to "D", spaced of 2 characters length. Renamed variables/files/class names to reflect better their role. Also updated console damage report to be written out more like in the OG:NT plugin's aside from the killer line. * fixes #1947 * fixes #1650 * fixes #1945 --- src/game/client/CMakeLists.txt | 7 +- src/game/client/cdll_client_int.cpp | 3 - ...er_damage_infos.h => c_neo_killer_infos.h} | 15 +- src/game/client/neo/c_neo_player.cpp | 2 +- .../client/neo/neo_killer_damage_info.cpp | 151 ++++ .../client/neo/ui/neo_hud_deathnotice.cpp | 2 +- .../neo/ui/neo_hud_killer_damage_info.cpp | 680 ------------------ .../neo/ui/neo_hud_killer_damage_info.h | 61 -- .../client/neo/ui/neo_hud_killer_info.cpp | 208 ++++++ src/game/client/neo/ui/neo_hud_killer_info.h | 34 + src/game/client/neo/ui/neo_root_settings.cpp | 10 - src/game/client/neo/ui/neo_root_settings.h | 2 - src/game/client/neo/ui/neo_scoreboard.cpp | 138 +++- src/game/client/neo/ui/neo_scoreboard.h | 25 +- src/game/client/neo/ui/neo_ui.h | 1 + src/game/server/neo/neo_player.cpp | 2 +- src/game/shared/hl2/hl2_usermessages.cpp | 4 +- 17 files changed, 539 insertions(+), 806 deletions(-) rename src/game/client/neo/{c_neo_killer_damage_infos.h => c_neo_killer_infos.h} (59%) create mode 100644 src/game/client/neo/neo_killer_damage_info.cpp delete mode 100644 src/game/client/neo/ui/neo_hud_killer_damage_info.cpp delete mode 100644 src/game/client/neo/ui/neo_hud_killer_damage_info.h create mode 100644 src/game/client/neo/ui/neo_hud_killer_info.cpp create mode 100644 src/game/client/neo/ui/neo_hud_killer_info.h diff --git a/src/game/client/CMakeLists.txt b/src/game/client/CMakeLists.txt index 9902db9f94..18d20326f5 100644 --- a/src/game/client/CMakeLists.txt +++ b/src/game/client/CMakeLists.txt @@ -1574,6 +1574,7 @@ set(UNITY_SOURCE_NEO_SRC_CLIENT neo/c_neo_te_tocflash.cpp neo/neo_fixup_glshaders.cpp neo/c_neo_bloom_controller.cpp + neo/neo_killer_damage_info.cpp ) set_source_files_properties( @@ -1590,7 +1591,7 @@ target_sources_grouped( neo/c_neo_player.h neo/c_neo_te_tocflash.h neo/neo_fixup_glshaders.h - neo/c_neo_killer_damage_infos.h + neo/c_neo_killer_infos.h ${UNITY_SOURCE_NEO_SRC_CLIENT} ) @@ -1658,7 +1659,7 @@ set(UNITY_SOURCE_NEO_UI neo/ui/neo_hud_ghost_beacons.cpp neo/ui/neo_hud_ghost_cap_point.cpp neo/ui/neo_hud_ghost_marker.cpp - neo/ui/neo_hud_killer_damage_info.cpp + neo/ui/neo_hud_killer_info.cpp neo/ui/neo_hud_ghost_uplink_state.cpp neo/ui/neo_hud_health_thermoptic_aux.cpp neo/ui/neo_hud_hint.cpp @@ -1697,7 +1698,7 @@ target_sources_grouped( neo/ui/neo_hud_ghost_beacons.h neo/ui/neo_hud_ghost_cap_point.h neo/ui/neo_hud_ghost_marker.h - neo/ui/neo_hud_killer_damage_info.h + neo/ui/neo_hud_killer_info.h neo/ui/neo_hud_ghost_uplink_state.h neo/ui/neo_hud_health_thermoptic_aux.h neo/ui/neo_hud_hint.h diff --git a/src/game/client/cdll_client_int.cpp b/src/game/client/cdll_client_int.cpp index b71354d9ea..b8692228b2 100644 --- a/src/game/client/cdll_client_int.cpp +++ b/src/game/client/cdll_client_int.cpp @@ -1445,9 +1445,6 @@ void CHLClient::PostInit() if (iCfgVerMajor < 22) { SetupBindIfNotSet("+attack3", MOUSE_MIDDLE); // Ping location - SetupBindIfNotSet("kdinfo_toggle", KEY_F11); // KD-info toggle - SetupBindIfNotSet("kdinfo_page_prev", KEY_P); // KD-info page previous - SetupBindIfNotSet("kdinfo_page_next", KEY_N); // KD-info page next SetupBindIfNotSet("neo_mp3", KEY_M); // MP3 player toggle // neo_aim_hold removal, +aim split to +aim and toggle_aim diff --git a/src/game/client/neo/c_neo_killer_damage_infos.h b/src/game/client/neo/c_neo_killer_infos.h similarity index 59% rename from src/game/client/neo/c_neo_killer_damage_infos.h rename to src/game/client/neo/c_neo_killer_infos.h index dbad5c24d3..e1d4876db8 100644 --- a/src/game/client/neo/c_neo_killer_damage_infos.h +++ b/src/game/client/neo/c_neo_killer_infos.h @@ -2,8 +2,10 @@ static constexpr const int WEP_NAME_MAXSTRLEN = 32; -struct CNEOKillerInfo +struct CNEOKillerInfos { + bool bHasDmgInfos = false; + wchar_t wszKillerName[MAX_PLAYER_NAME_LENGTH + 1]; int iEntIndex; int iClass; int iHP; @@ -11,12 +13,5 @@ struct CNEOKillerInfo wchar_t wszKilledWith[WEP_NAME_MAXSTRLEN]; }; -struct CNEOKillerDamageInfos -{ - bool bHasDmgInfos = false; - CNEOKillerInfo killerInfo; - wchar_t wszKillerName[MAX_PLAYER_NAME_LENGTH + 1]; -}; - -// Global instance of CNEOKillerDamageInfos -inline CNEOKillerDamageInfos g_neoKDmgInfos; +// Global instance of CNEOKillerInfos +inline CNEOKillerInfos g_neoKillerInfos; diff --git a/src/game/client/neo/c_neo_player.cpp b/src/game/client/neo/c_neo_player.cpp index f7d082a9f0..b15b3adbe3 100644 --- a/src/game/client/neo/c_neo_player.cpp +++ b/src/game/client/neo/c_neo_player.cpp @@ -48,7 +48,7 @@ #include "rendertexture.h" #include "ivieweffects.h" #include "iachievementmgr.h" -#include "c_neo_killer_damage_infos.h" +#include "c_neo_killer_infos.h" #include #include diff --git a/src/game/client/neo/neo_killer_damage_info.cpp b/src/game/client/neo/neo_killer_damage_info.cpp new file mode 100644 index 0000000000..52f88ad6e4 --- /dev/null +++ b/src/game/client/neo/neo_killer_damage_info.cpp @@ -0,0 +1,151 @@ +#include + +#include "c_neo_player.h" +#include "neo_gamerules.h" + +#include "c_user_message_register.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +// Console + activation of damage info +static void __MsgFunc_KillerDamageInfo(bf_read &msg) +{ + const int killerIdx = msg.ReadShort(); + char killedBy[MAX_PLAYER_NAME_LENGTH] = {}; + const bool foundKilledBy = msg.ReadString(killedBy, sizeof(killedBy), false); + + auto *localPlayer = C_NEO_Player::GetLocalNEOPlayer(); + if (!localPlayer) + { + return; + } + + // Print damage stats into the console + // Print to console + AttackersTotals totals = {}; + + const int thisIdx = localPlayer->entindex(); + + // Can't rely on Msg as it can print out of order, so do it in chunks + static char killByLine[512]; + + static const char *BORDER = "==========================\n"; + bool setKillByLine = false; + + V_memset(&g_neoKillerInfos, 0, sizeof(CNEOKillerInfos)); + g_neoKillerInfos.bHasDmgInfos = true; + + if (killerIdx > 0 && killerIdx <= gpGlobals->maxClients) + { + auto *neoAttacker = assert_cast(UTIL_PlayerByIndex(killerIdx)); + if (neoAttacker && neoAttacker->entindex() != thisIdx) + { + g_pVGuiLocalize->ConvertANSIToUnicode(neoAttacker->GetNeoPlayerName(), + g_neoKillerInfos.wszKillerName, + sizeof(g_neoKillerInfos.wszKillerName)); + } + // If not neoAttacker, already cleared out by memset earlier + + if (neoAttacker) + { + g_neoKillerInfos.iEntIndex = killerIdx; + g_neoKillerInfos.iClass = neoAttacker->GetClass(); + g_neoKillerInfos.iHP = neoAttacker->GetHealth(); + g_neoKillerInfos.flDistance = METERS_PER_INCH * neoAttacker->GetAbsOrigin().DistTo(localPlayer->GetAbsOrigin()); + if (foundKilledBy) + { + // Rename very long names + if (V_strcmp(killedBy, "MURATA SUPA 7") == 0) + { + V_strcpy_safe(killedBy, "SUPA 7"); + } + g_pVGuiLocalize->ConvertANSIToUnicode(killedBy, + g_neoKillerInfos.wszKilledWith, + sizeof(g_neoKillerInfos.wszKilledWith)); + } + else + { + g_neoKillerInfos.wszKilledWith[0] = L'\0'; + } + + V_sprintf_safe(killByLine, "Killed by: %s [%s | %d hp] with %s at %.0f m\n", + neoAttacker->GetNeoPlayerName(), GetNeoClassName(g_neoKillerInfos.iClass), + g_neoKillerInfos.iHP, foundKilledBy ? killedBy : "", g_neoKillerInfos.flDistance); + setKillByLine = true; + } + } + else if (killerIdx == NEO_ENVIRON_KILLED) + { + g_neoKillerInfos.iEntIndex = NEO_ENVIRON_KILLED; + g_neoKillerInfos.wszKilledWith[0] = L'\0'; + if (foundKilledBy && V_strcmp(killedBy, "neo_npc_targetsystem") == 0) + { + const char *pszMap = IGameSystem::MapName(); + if (V_strcmp(pszMap, "ntre_rogue_ctg") == 0) + { + V_wcscpy_safe(g_neoKillerInfos.wszKilledWith, L"184-J IFV"); + } + else + { + V_wcscpy_safe(g_neoKillerInfos.wszKilledWith, L"Turret"); + } + } + } + + ConMsg("%sDamage infos (Round %d):\n%s\n", BORDER, NEORules()->roundNumber(), setKillByLine ? killByLine : ""); + + for (int pIdx = 1; pIdx <= gpGlobals->maxClients; ++pIdx) + { + if (pIdx == thisIdx) + { + continue; + } + + auto* neoAttacker = assert_cast(UTIL_PlayerByIndex(pIdx)); + if (!neoAttacker || neoAttacker->IsHLTV()) + { + continue; + } + + const char *dmgerName = neoAttacker->GetNeoPlayerName(); + if (!dmgerName) + { + continue; + } + + const AttackersTotals attackerInfo = { + .dealtDmgs = neoAttacker->GetAttackersScores(thisIdx), + .dealtHits = neoAttacker->GetAttackerHits(thisIdx), + .takenDmgs = localPlayer->GetAttackersScores(pIdx), + .takenHits = localPlayer->GetAttackerHits(pIdx), + }; + if (attackerInfo.dealtDmgs > 0 || attackerInfo.takenDmgs > 0) + { + const char *dmgerClass = GetNeoClassName(neoAttacker->GetClass()); + + char infoLine[128] = {}; + if (attackerInfo.dealtDmgs > 0) + { + V_sprintf_safe(infoLine, "Damage dealt to %s [%s]: %d in %d hits\n", + dmgerName, dmgerClass, + attackerInfo.dealtDmgs, attackerInfo.dealtHits); + } + if (attackerInfo.takenDmgs > 0) + { + V_sprintf_safe(infoLine, "Damage taken from %s [%s]: %d in %d hits\n", + dmgerName, dmgerClass, + attackerInfo.takenDmgs, attackerInfo.takenHits); + } + ConMsg("%s", infoLine); + + totals += attackerInfo; + } + } + + ConMsg("Total damage dealt: %d in %d hits\nTotal damage received from players: %d in %d hits\n%s\n", + totals.dealtDmgs, totals.dealtHits, + totals.takenDmgs, totals.takenHits, + BORDER); +} +USER_MESSAGE_REGISTER(KillerDamageInfo); diff --git a/src/game/client/neo/ui/neo_hud_deathnotice.cpp b/src/game/client/neo/ui/neo_hud_deathnotice.cpp index ba16f6f24e..20da8db0cd 100644 --- a/src/game/client/neo/ui/neo_hud_deathnotice.cpp +++ b/src/game/client/neo/ui/neo_hud_deathnotice.cpp @@ -22,7 +22,7 @@ #include "neo_hud_childelement.h" #include "spectatorgui.h" #include "takedamageinfo.h" -#include "c_neo_killer_damage_infos.h" +#include "c_neo_killer_infos.h" #include "neo_scoreboard.h" // memdbgon must be the last include file in a .cpp file!!! diff --git a/src/game/client/neo/ui/neo_hud_killer_damage_info.cpp b/src/game/client/neo/ui/neo_hud_killer_damage_info.cpp deleted file mode 100644 index 2ee0ad0d38..0000000000 --- a/src/game/client/neo/ui/neo_hud_killer_damage_info.cpp +++ /dev/null @@ -1,680 +0,0 @@ -#include -#include "neo_hud_killer_damage_info.h" - -#include "iclientmode.h" - -#include "c_neo_player.h" -#include "neo_gamerules.h" -#include "vgui/ILocalize.h" -#include "inputsystem/iinputsystem.h" -#include "IGameUIFuncs.h" -#include "neo_misc.h" -#include "c_user_message_register.h" -#include "igamesystem.h" -#include "neo_theme.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -ConVar cl_neo_kdinfo_toggletype("cl_neo_kdinfo_toggletype", "0", FCVAR_ARCHIVE, - "Killer damage information HUD toggle behavior, 0 = always auto show, 1 = auto show based on toggle per match, 2 = never auto show", - true, 0.0f, true, static_cast(KDMGINFO_TOGGLETYPE__TOTAL - 1)); -ConVar cl_neo_kdinfo_debug_show_local("cl_neo_kdinfo_debug_show_local", "0", FCVAR_CHEAT | FCVAR_HIDDEN | FCVAR_DONTRECORD, "Killer damage information HUD debug: Show local player in the list"); - -DECLARE_NAMED_HUDELEMENT(CNEOHud_KillerDamageInfo, neo_killer_damage_info) - -NEO_HUD_ELEMENT_DECLARE_FREQ_CVAR(KillerDamageInfo, 0.1) - -static const constexpr float UI_SCALE = 0.66f; - -CON_COMMAND_F(kdinfo_toggle, "Toggle killer damage info", FCVAR_USERINFO) -{ - CNEOHud_KillerDamageInfo *pKillerDmgInfo = GET_NAMED_HUDELEMENT(CNEOHud_KillerDamageInfo, neo_killer_damage_info); - Assert(pKillerDmgInfo); - if (!pKillerDmgInfo) - { - return; - } - - // Only allow toggle when it's at a state available to show the stats - // to the player - if (pKillerDmgInfo->m_bPlayerShownHud) - { - pKillerDmgInfo->m_bShowInfo = !pKillerDmgInfo->m_bShowInfo; - } -} - -CON_COMMAND_F(kdinfo_page_prev, "Killer damage info previous page", FCVAR_USERINFO) -{ - CNEOHud_KillerDamageInfo *pKillerDmgInfo = GET_NAMED_HUDELEMENT(CNEOHud_KillerDamageInfo, neo_killer_damage_info); - Assert(pKillerDmgInfo); - if (!pKillerDmgInfo) - { - return; - } - - pKillerDmgInfo->m_iCurPage = LoopAroundInArray(pKillerDmgInfo->m_iCurPage - 1, pKillerDmgInfo->m_iTotalPages); -} - -CON_COMMAND_F(kdinfo_page_next, "Killer damage info next page", FCVAR_USERINFO) -{ - CNEOHud_KillerDamageInfo *pKillerDmgInfo = GET_NAMED_HUDELEMENT(CNEOHud_KillerDamageInfo, neo_killer_damage_info); - Assert(pKillerDmgInfo); - if (!pKillerDmgInfo) - { - return; - } - - pKillerDmgInfo->m_iCurPage = LoopAroundInArray(pKillerDmgInfo->m_iCurPage + 1, pKillerDmgInfo->m_iTotalPages); -} - -static constexpr int PAGE_MAX_SHOW = 6; - -CNEOHud_KillerDamageInfo::CNEOHud_KillerDamageInfo(const char *pszName, vgui::Panel *parent) - : CHudElement(pszName) - , vgui::Panel(parent, pszName) -{ - SetAutoDelete(true); - SetupNTRETheme(&m_uiCtx); - m_uiCtx.colors.sectionBg = COLOR_TRANSPARENT; - - if (parent) - { - SetParent(parent); - } - else - { - SetParent(g_pClientMode->GetViewport()); - } - - int wide, tall; - vgui::surface()->GetScreenSize(wide, tall); - SetBounds(0, 0, wide, tall); - - SetVisible(false); - - SetFgColor(COLOR_TRANSPARENT); - SetBgColor(COLOR_TRANSPARENT); -} - -CNEOHud_KillerDamageInfo::~CNEOHud_KillerDamageInfo() -{ - NeoUI::FreeContext(&m_uiCtx); -} - -void CNEOHud_KillerDamageInfo::ApplySchemeSettings(vgui::IScheme *pScheme) -{ - vgui::Panel::ApplySchemeSettings(pScheme); - - m_hFont = pScheme->GetFont("NHudOCRSmall", true); - - int wide, tall; - vgui::surface()->GetScreenSize(wide, tall); - SetBounds(0, 0, wide, tall); - - SetFgColor(COLOR_TRANSPARENT); - SetBgColor(COLOR_TRANSPARENT); - - static constexpr const char *FONT_NAMES[NeoUI::FONT__TOTAL] = { - "NeoUISmall", "NHudOCR", "NHudOCRSmallerNoAdditive", "ClientTitleFont", "ClientTitleFontSmall", - "NeoUINormal" - }; - for (int i = 0; i < NeoUI::FONT__TOTAL; ++i) - { - m_uiCtx.fonts[i].hdl = pScheme->GetFont(FONT_NAMES[i], true); - } - - // NEO TODO (nullsystem): #1253 - Unify layout sizing with root + loading - m_uiCtx.layout.iDefRowTall = (tall / 27) * UI_SCALE; - m_uiCtx.iMarginX = (wide / 192) * UI_SCALE; - m_uiCtx.iMarginY = (tall / 108) * UI_SCALE; -} - -void CNEOHud_KillerDamageInfo::resetHUDState() -{ - V_memset(&g_neoKDmgInfos, 0, sizeof(CNEOKillerDamageInfos)); - for (int pIdx = 1; pIdx <= gpGlobals->maxClients; ++pIdx) - { - auto *neoAttacker = dynamic_cast(UTIL_PlayerByIndex(pIdx)); - if (neoAttacker) - { - for (int i = 0; i < MAX_PLAYERS_ARRAY_SAFE; ++i) - { - neoAttacker->m_rfAttackersScores.GetForModify(i) = 0; - neoAttacker->m_rfAttackersAccumlator.GetForModify(i) = 0.0f; - neoAttacker->m_rfAttackersHits.GetForModify(i) = 0; - } - } - } - - C_NEO_Player *localPlayer = C_NEO_Player::GetLocalNEOPlayer(); - if (localPlayer) - { - V_memset(localPlayer->m_rfNeoPlayerIdxsKilledByLocal, false, sizeof(localPlayer->m_rfNeoPlayerIdxsKilledByLocal)); - } - ResetDisplayInfos(); -} - -void CNEOHud_KillerDamageInfo::ResetDisplayInfos() -{ - m_iCurPage = 0; - m_iTotalPages = 0; - m_iAttackersTotalsSize = 0; - V_memset(m_attackersTotals, 0, sizeof(m_attackersTotals)); - V_memset(m_wszDmgerNamesList, 0, sizeof(m_wszDmgerNamesList)); -} - -void CNEOHud_KillerDamageInfo::UpdateStateForNeoHudElementDraw() -{ - const C_NEO_Player *localPlayer = C_NEO_Player::GetLocalNEOPlayer(); - - if (m_iTotalPages > 0 && NEORules()->GetRoundStatus() == NeoRoundStatus::PreRoundFreeze) - { - resetHUDState(); - } - - // If to show or not - const bool bNextPlayerShownHud = localPlayer - && (const_cast(localPlayer)->IsPlayerDead() && g_neoKDmgInfos.bHasDmgInfos) - && (localPlayer->GetTeamNumber() == TEAM_JINRAI || - localPlayer->GetTeamNumber() == TEAM_NSF); - const bool bToShownStateChanged = (bNextPlayerShownHud && bNextPlayerShownHud != m_bPlayerShownHud); - m_bPlayerShownHud = bNextPlayerShownHud; - if (!bToShownStateChanged) - { - // Only bother processing when changing towards showing state - return; - } - - const int iTT = cl_neo_kdinfo_toggletype.GetInt(); - m_bShowInfo = - (iTT == KDMGINFO_TOGGLETYPE_ROUND || iTT == KDMGINFO_TOGGLETYPE_NEVER) ? - (iTT == KDMGINFO_TOGGLETYPE_ROUND) : - m_bShowInfo; - - // Text infos - V_swprintf_safe(m_wszKillerTitle, L"Killed by %ls", g_neoKDmgInfos.wszKillerName); - - // UI sizing - int iScrWide, iScrTall; - vgui::surface()->GetScreenSize(iScrWide, iScrTall); - // This is the baseline width, text width will determine full width - const int iBaselineWide = (iScrWide / 3) * UI_SCALE; - m_uiCtx.dPanel.tall = (iScrTall / 2) * UI_SCALE; - m_uiCtx.dPanel.x = xpos; - m_uiCtx.dPanel.y = ypos; - - // Set keybind message - { - wchar_t wszBindToggle[32] = {}; - wchar_t wszBindPrev[32] = {}; - wchar_t wszBindNext[32] = {}; - - const char *szBindToggle = g_pInputSystem->ButtonCodeToString(gameuifuncs->GetButtonCodeForBind("kdinfo_toggle")); - const char *szBindPrev = g_pInputSystem->ButtonCodeToString(gameuifuncs->GetButtonCodeForBind("kdinfo_page_prev")); - const char *szBindNext = g_pInputSystem->ButtonCodeToString(gameuifuncs->GetButtonCodeForBind("kdinfo_page_next")); - - g_pVGuiLocalize->ConvertANSIToUnicode(szBindToggle, wszBindToggle, sizeof(wszBindToggle)); - g_pVGuiLocalize->ConvertANSIToUnicode(szBindPrev, wszBindPrev, sizeof(wszBindPrev)); - g_pVGuiLocalize->ConvertANSIToUnicode(szBindNext, wszBindNext, sizeof(wszBindNext)); - - V_swprintf_safe(m_wszBindBtnMsg, L"Toggle: [%ls] | Previous: [%ls] | Next: [%ls]", - wszBindToggle, wszBindPrev, wszBindNext); - } - - ResetDisplayInfos(); - - int iMaxNameTextWidth = 0; - for (int pIdx = 1; pIdx <= gpGlobals->maxClients && m_iAttackersTotalsSize < MAX_PLAYERS; ++pIdx) - { - if (!cl_neo_kdinfo_debug_show_local.GetBool() && pIdx == localPlayer->entindex()) - { - continue; - } - - auto *neoAttacker = dynamic_cast(UTIL_PlayerByIndex(pIdx)); - if (!neoAttacker || neoAttacker->IsHLTV()) - { - continue; - } - - const AttackersTotals attackerInfo = { - .dealtDmgs = neoAttacker->GetAttackersScores(localPlayer->entindex()), - .dealtHits = neoAttacker->GetAttackerHits(localPlayer->entindex()), - .takenDmgs = localPlayer->GetAttackersScores(pIdx), - .takenHits = localPlayer->GetAttackerHits(pIdx), - }; - const char *dmgerName = neoAttacker->GetNeoPlayerName(); - const int iDmgerTeam = neoAttacker->GetTeamNumber(); -#define DEBUG_SHOW_ALL (0) -#if DEBUG_SHOW_ALL - if (dmgerName) -#else - if (dmgerName && (attackerInfo.dealtDmgs > 0 || attackerInfo.takenDmgs > 0) && - (iDmgerTeam == TEAM_JINRAI || iDmgerTeam == TEAM_NSF)) -#endif - { - m_attackersTotals[m_iAttackersTotalsSize] = attackerInfo; - g_pVGuiLocalize->ConvertANSIToUnicode(dmgerName, - m_wszDmgerNamesList[m_iAttackersTotalsSize], - sizeof(m_wszDmgerNamesList[m_iAttackersTotalsSize])); - m_iClassIdxsList[m_iAttackersTotalsSize] = neoAttacker->GetClass(); - m_iTeamIdxsList[m_iAttackersTotalsSize] = iDmgerTeam; - m_iEntIdxsList[m_iAttackersTotalsSize] = pIdx; - - int iFontTextWidth = 0, iFontTextHeight = 0; - vgui::surface()->GetTextSize(m_uiCtx.fonts[NeoUI::FONT_NTHORIZSIDES].hdl, - m_wszDmgerNamesList[m_iAttackersTotalsSize], - iFontTextWidth, iFontTextHeight); - iMaxNameTextWidth = Max(iMaxNameTextWidth, iFontTextWidth); - - ++m_iAttackersTotalsSize; - } - } - m_iTotalPages = (m_iAttackersTotalsSize > 0) ? - (1 + (static_cast(m_iAttackersTotalsSize - 1) / PAGE_MAX_SHOW)) : - 0; - m_uiCtx.dPanel.wide = Max(iBaselineWide, static_cast(((iScrWide / 6) * UI_SCALE) + iMaxNameTextWidth + (2 * m_uiCtx.iMarginX))); -} - -void CNEOHud_KillerDamageInfo::DrawNeoHudElement() -{ - if (!ShouldDraw() || !m_bPlayerShownHud || !m_bShowInfo) - { - return; - } - - const int iKillerEntIndex = g_neoKDmgInfos.killerInfo.iEntIndex; - - const bool bHasKiller = iKillerEntIndex > 0 || iKillerEntIndex == NEO_ENVIRON_KILLED; - const bool bHasPages = m_iTotalPages > 0; - - const bool bHasInfo = bHasPages || bHasKiller; - if (!bHasInfo) - { - return; - } - - const C_NEO_Player *localPlayer = C_NEO_Player::GetLocalNEOPlayer(); - - // Show info box while it just died spectating itself - m_uiCtx.colors.normalFg = COLOR_WHITE; - DrawNeoHudRoundedBox(m_uiCtx.dPanel.x, m_uiCtx.dPanel.y, m_uiCtx.dPanel.x + m_uiCtx.dPanel.wide, m_uiCtx.dPanel.y + m_uiCtx.dPanel.tall, m_boxColor); - NeoUI::BeginContext(&m_uiCtx, NeoUI::MODE_PAINT, nullptr, "NeoHudKillerDmgInfo"); - NeoUI::BeginSection(NeoUI::SECTIONFLAG_LABELPANELVIEWPORT); - { - NeoUI::SwapFont(NeoUI::FONT_NTHORIZSIDES); - m_uiCtx.eLabelTextStyle = NeoUI::TEXTSTYLE_CENTER; - NeoUI::Label(m_wszBindBtnMsg); - m_uiCtx.eLabelTextStyle = NeoUI::TEXTSTYLE_LEFT; - - if (bHasKiller) - { - NeoUI::SwapFont(NeoUI::FONT_NTHORIZSIDES); - const bool bSuicideKill = (iKillerEntIndex == localPlayer->entindex()) || - (iKillerEntIndex == NEO_ENVIRON_KILLED); - // Killer title - { - NeoUI::SetPerRowLayout(1); - if (bSuicideKill) - { - // Show a suicide/self kill if it matches the local player's entity index - NeoUI::HeadingLabel(L"YOU KILLED YOURSELF"); - } - else - { - NeoUI::HeadingLabel(m_wszKillerTitle); - } - } - - NeoUI::SwapFont(NeoUI::FONT_NTHORIZSIDES); - static wchar_t wszLabel[32] = {}; - - // Killer info - if (bSuicideKill) - { - // Just state the weapon, the rest of the infos aren't useful - NeoUI::SetPerRowLayout(1); - m_uiCtx.eLabelTextStyle = NeoUI::TEXTSTYLE_CENTER; - - // If it's an empty weapon, it's generally a suicide command kill - if (iKillerEntIndex == NEO_ENVIRON_KILLED) - { - if (g_neoKDmgInfos.killerInfo.wszKilledWith[0] != L'\0') - { - V_swprintf_safe(wszLabel, L"Killed by %ls", g_neoKDmgInfos.killerInfo.wszKilledWith); - NeoUI::Label(wszLabel); - } - else - { - NeoUI::Label(L"Killed by map"); - } - } - else if (g_neoKDmgInfos.killerInfo.wszKilledWith[0] == L'\0') - { - NeoUI::Label(L"Killed by map or used suicide command"); - } - else - { - V_swprintf_safe(wszLabel, L"Weapon: %ls", g_neoKDmgInfos.killerInfo.wszKilledWith); - NeoUI::Label(wszLabel); - } - } - else - { - const int iTmpMarginX = m_uiCtx.iMarginX; - m_uiCtx.iMarginX = m_uiCtx.iMarginX / 3; - - NeoUI::SetPerRowLayout(4); - NeoUI::SetPerCellVertLayout(2); - - m_uiCtx.eLabelTextStyle = NeoUI::TEXTSTYLE_RIGHT; - NeoUI::Label(L"Health:"); - NeoUI::Label(L"Distance:"); - - m_uiCtx.eLabelTextStyle = NeoUI::TEXTSTYLE_LEFT; - V_swprintf_safe(wszLabel, L"%d", g_neoKDmgInfos.killerInfo.iHP); - NeoUI::Label(wszLabel); - V_swprintf_safe(wszLabel, L"%.2fm", g_neoKDmgInfos.killerInfo.flDistance); - NeoUI::Label(wszLabel); - - m_uiCtx.eLabelTextStyle = NeoUI::TEXTSTYLE_RIGHT; - NeoUI::Label(L"Class:"); - NeoUI::Label(L"Weapon:"); - - m_uiCtx.eLabelTextStyle = NeoUI::TEXTSTYLE_LEFT; - V_swprintf_safe(wszLabel, L"%ls", GetNeoClassNameW(g_neoKDmgInfos.killerInfo.iClass)); - NeoUI::Label(wszLabel); - V_swprintf_safe(wszLabel, L"%ls", g_neoKDmgInfos.killerInfo.wszKilledWith); - NeoUI::Label(wszLabel); - - NeoUI::SetPerCellVertLayout(0); - - m_uiCtx.iMarginX = iTmpMarginX; - } - } - - // General infos - Inflicted damage, name, received damage - if (bHasPages) - { - // Display pagination info if more than 1 page - if (m_iTotalPages > 1) - { - m_uiCtx.eLabelTextStyle = NeoUI::TEXTSTYLE_CENTER; - NeoUI::SwapFont(NeoUI::FONT_NTHORIZSIDES); - NeoUI::SetPerRowLayout(1); - - static wchar_t wszPageLabel[32] = {}; - V_swprintf_safe(wszPageLabel, L"(Page %d/%d)", m_iCurPage + 1, m_iTotalPages); - NeoUI::Label(wszPageLabel); - } - - // General attacker/inflictor infos - static const int ROWLAYOUT_INFO[] = { - 25, 50, -1 - }; - NeoUI::SetPerRowLayout(ARRAYSIZE(ROWLAYOUT_INFO), ROWLAYOUT_INFO); - m_uiCtx.eLabelTextStyle = NeoUI::TEXTSTYLE_CENTER; - - const int iCurPageStart = m_iCurPage * PAGE_MAX_SHOW; - const int iCurPageEnd = Min(m_iAttackersTotalsSize, iCurPageStart + PAGE_MAX_SHOW); - - const int iYOffsetting = (m_uiCtx.layout.iRowTall / 4); - m_uiCtx.iLayoutY += iYOffsetting; - - NeoUI::SwapFont(NeoUI::FONT_NTHORIZSIDES); - NeoUI::Label(L"Dealt to"); - NeoUI::Label(L"Player"); - NeoUI::Label(L"Taken from"); - - m_uiCtx.iLayoutY -= iYOffsetting; - - NeoUI::SetPerCellVertLayout(2); - - for (int i = iCurPageStart; i < iCurPageEnd; ++i) - { - const auto &attackerInfo = m_attackersTotals[i]; - if (attackerInfo.dealtDmgs <= 0 && attackerInfo.takenDmgs <= 0) - { - continue; - } - - static wchar_t wszLabel[64] = {}; - - const int iAttackerEntIdx = m_iEntIdxsList[i]; - const bool bKilledYou = (iAttackerEntIdx == iKillerEntIndex); - const bool bYouKilled = localPlayer->m_rfNeoPlayerIdxsKilledByLocal[iAttackerEntIdx]; - - // Left side - Dealt damages + hits to attacker - if (attackerInfo.dealtDmgs > 0) - { - NeoUI::SwapFont(NeoUI::FONT_NTHORIZSIDES); - if (bYouKilled) - { - m_uiCtx.colors.normalFg = COLOR_RED; - } - V_swprintf_safe(wszLabel, L"%d", attackerInfo.dealtDmgs); - NeoUI::Label(wszLabel); - m_uiCtx.colors.normalFg = COLOR_WHITE; - - NeoUI::SwapFont(NeoUI::FONT_NTHORIZSIDES); - V_swprintf_safe(wszLabel, L"%d hit%ls", attackerInfo.dealtHits, (attackerInfo.dealtHits <= 1) ? L"" : L"s"); - NeoUI::Label(wszLabel); - } - else - { - NeoUI::Pad(); - NeoUI::Pad(); - } - - // Middle - Attacker's name and class - { - const int iDmgerTeam = m_iTeamIdxsList[i]; - Assert(iDmgerTeam == TEAM_JINRAI || iDmgerTeam == TEAM_NSF); - - NeoUI::SwapFont(NeoUI::FONT_NTHORIZSIDES); - m_uiCtx.colors.normalFg = (iDmgerTeam == TEAM_JINRAI) ? COLOR_JINRAI : COLOR_NSF; - NeoUI::Label(m_wszDmgerNamesList[i]); - m_uiCtx.colors.normalFg = COLOR_WHITE; - - NeoUI::Label(GetNeoClassNameW(m_iClassIdxsList[i])); - } - - // Right side - Taken damages + hits from attacker - if (attackerInfo.takenDmgs > 0) - { - NeoUI::SwapFont(NeoUI::FONT_NTHORIZSIDES); - if (bKilledYou) - { - m_uiCtx.colors.normalFg = COLOR_RED; - } - V_swprintf_safe(wszLabel, L"%d", attackerInfo.takenDmgs); - NeoUI::Label(wszLabel); - m_uiCtx.colors.normalFg = COLOR_WHITE; - - NeoUI::SwapFont(NeoUI::FONT_NTHORIZSIDES); - V_swprintf_safe(wszLabel, L"%d hit%ls", attackerInfo.takenHits, (attackerInfo.takenHits <= 1) ? L"" : L"s"); - NeoUI::Label(wszLabel); - } - else - { - NeoUI::Pad(); - NeoUI::Pad(); - } - - m_uiCtx.iLayoutY += iYOffsetting; - } - - NeoUI::SetPerCellVertLayout(0); - } - } - NeoUI::EndSection(); - NeoUI::EndContext(); -} - -void CNEOHud_KillerDamageInfo::Paint() -{ - SetFgColor(COLOR_TRANSPARENT); - SetBgColor(COLOR_TRANSPARENT); - vgui::Panel::Paint(); - PaintNeoElement(); -} - -// Console + activation of damage info -static void __MsgFunc_DamageInfo(bf_read& msg) -{ - const int killerIdx = msg.ReadShort(); - char killedBy[MAX_PLAYER_NAME_LENGTH] = {}; - const bool foundKilledBy = msg.ReadString(killedBy, sizeof(killedBy), false); - - auto *localPlayer = C_NEO_Player::GetLocalNEOPlayer(); - if (!localPlayer) - { - return; - } - - // Print damage stats into the console - // Print to console - AttackersTotals totals = {}; - - const int thisIdx = localPlayer->entindex(); - - // Can't rely on Msg as it can print out of order, so do it in chunks - static char killByLine[512]; - - static const char* BORDER = "==========================\n"; - bool setKillByLine = false; - - V_memset(&g_neoKDmgInfos, 0, sizeof(CNEOKillerDamageInfos)); - g_neoKDmgInfos.bHasDmgInfos = true; - - if (killerIdx > 0 && killerIdx <= gpGlobals->maxClients) - { - auto *neoAttacker = assert_cast(UTIL_PlayerByIndex(killerIdx)); - if (neoAttacker && neoAttacker->entindex() != thisIdx) - { - g_pVGuiLocalize->ConvertANSIToUnicode(neoAttacker->GetNeoPlayerName(), - g_neoKDmgInfos.wszKillerName, - sizeof(g_neoKDmgInfos.wszKillerName)); - } - // If not neoAttacker, already cleared out by memset earlier - - if (neoAttacker) - { - CNEOKillerInfo killerInfo = { - .iEntIndex = killerIdx, - .iClass = neoAttacker->GetClass(), - .iHP = neoAttacker->GetHealth(), - .flDistance = METERS_PER_INCH * neoAttacker->GetAbsOrigin().DistTo(localPlayer->GetAbsOrigin()), - }; - g_neoKDmgInfos.killerInfo = killerInfo; - if (foundKilledBy) - { - // Rename very long names - if (V_strcmp(killedBy, "MURATA SUPA 7") == 0) - { - V_strcpy_safe(killedBy, "SUPA 7"); - } - g_pVGuiLocalize->ConvertANSIToUnicode(killedBy, - g_neoKDmgInfos.killerInfo.wszKilledWith, - sizeof(g_neoKDmgInfos.killerInfo.wszKilledWith)); - } - else - { - g_neoKDmgInfos.killerInfo.wszKilledWith[0] = L'\0'; - } - - V_sprintf_safe(killByLine, "Killed by: %s [%s | %d hp] with %s at %.0f m\n", - neoAttacker->GetNeoPlayerName(), GetNeoClassName(killerInfo.iClass), - killerInfo.iHP, foundKilledBy ? killedBy : "", killerInfo.flDistance); - setKillByLine = true; - } - } - else if (killerIdx == NEO_ENVIRON_KILLED) - { - g_neoKDmgInfos.killerInfo.iEntIndex = NEO_ENVIRON_KILLED; - g_neoKDmgInfos.killerInfo.wszKilledWith[0] = L'\0'; - if (foundKilledBy && V_strcmp(killedBy, "neo_npc_targetsystem") == 0) - { - const char *pszMap = IGameSystem::MapName(); - if (V_strcmp(pszMap, "ntre_rogue_ctg") == 0) - { - V_wcscpy_safe(g_neoKDmgInfos.killerInfo.wszKilledWith, L"184-J IFV"); - } - else - { - V_wcscpy_safe(g_neoKDmgInfos.killerInfo.wszKilledWith, L"Turret"); - } - } - } - - ConMsg("%sDamage infos (Round %d):\n%s\n", BORDER, NEORules()->roundNumber(), setKillByLine ? killByLine : ""); - - for (int pIdx = 1; pIdx <= gpGlobals->maxClients; ++pIdx) - { - if (pIdx == thisIdx) - { - continue; - } - - auto* neoAttacker = assert_cast(UTIL_PlayerByIndex(pIdx)); - if (!neoAttacker || neoAttacker->IsHLTV()) - { - continue; - } - - const char *dmgerName = neoAttacker->GetNeoPlayerName(); - if (!dmgerName) - { - continue; - } - - const AttackersTotals attackerInfo = { - .dealtDmgs = neoAttacker->GetAttackersScores(thisIdx), - .dealtHits = neoAttacker->GetAttackerHits(thisIdx), - .takenDmgs = localPlayer->GetAttackersScores(pIdx), - .takenHits = localPlayer->GetAttackerHits(pIdx), - }; - if (attackerInfo.dealtDmgs > 0 || attackerInfo.takenDmgs > 0) - { - const char *dmgerClass = GetNeoClassName(neoAttacker->GetClass()); - - char infoLine[128] = {}; - if (attackerInfo.dealtDmgs > 0 && attackerInfo.takenDmgs > 0) - { - V_sprintf_safe(infoLine, "%s [%s]: Dealt to: %d in %d hits | Taken from: %d in %d hits\n", - dmgerName, dmgerClass, - attackerInfo.dealtDmgs, attackerInfo.dealtHits, attackerInfo.takenDmgs, attackerInfo.takenHits); - } - else if (attackerInfo.dealtDmgs > 0) - { - V_sprintf_safe(infoLine, "%s [%s]: Dealt to: %d in %d hits\n", - dmgerName, dmgerClass, - attackerInfo.dealtDmgs, attackerInfo.dealtHits); - } - else if (attackerInfo.takenDmgs > 0) - { - V_sprintf_safe(infoLine, "%s [%s]: Taken from: %d in %d hits\n", - dmgerName, dmgerClass, - attackerInfo.takenDmgs, attackerInfo.takenHits); - } - else - { - // Should never happen since the if statement one-level - // above prevents this anyway - Assert(false); - } - - ConMsg("%s", infoLine); - - totals += attackerInfo; - } - } - - ConMsg("\nTOTAL: Dealt: %d in %d hits | Taken: %d in %d hits\n%s\n", - totals.dealtDmgs, totals.dealtHits, - totals.takenDmgs, totals.takenHits, - BORDER); -} -USER_MESSAGE_REGISTER(DamageInfo); - diff --git a/src/game/client/neo/ui/neo_hud_killer_damage_info.h b/src/game/client/neo/ui/neo_hud_killer_damage_info.h deleted file mode 100644 index ef7b81b58f..0000000000 --- a/src/game/client/neo/ui/neo_hud_killer_damage_info.h +++ /dev/null @@ -1,61 +0,0 @@ -#pragma once - -#include "neo_hud_childelement.h" -#include "c_neo_killer_damage_infos.h" -#include "neo_ui.h" -#include "neo_player_shared.h" - -#include -#include - -enum KDMGINFO_TOGGLETYPE -{ - KDMGINFO_TOGGLETYPE_ROUND = 0, // m_bShowInfo always set to true on death/end, per round toggle - KDMGINFO_TOGGLETYPE_MATCH, // m_bShowInfo not changed on death/end, per match toggle - KDMGINFO_TOGGLETYPE_NEVER, // m_bShowInfo always set to false on death/end, per round toggle - - KDMGINFO_TOGGLETYPE__TOTAL, -}; - -class CNEOHud_KillerDamageInfo : public CNEOHud_ChildElement, public CHudElement, public vgui::Panel -{ - DECLARE_CLASS_SIMPLE(CNEOHud_KillerDamageInfo, CNEOHud_ChildElement) - -public: - CNEOHud_KillerDamageInfo(const char *pszName, vgui::Panel *parent = nullptr); - ~CNEOHud_KillerDamageInfo(); - - void ApplySchemeSettings(vgui::IScheme *pScheme) override; - void Paint() override; - void resetHUDState() override; - - bool m_bShowInfo = true; - bool m_bPlayerShownHud = false; - - int m_iTotalPages = 0; - int m_iCurPage = 0; - -protected: - void UpdateStateForNeoHudElementDraw() override; - void DrawNeoHudElement() override; - ConVar *GetUpdateFrequencyConVar() const override; - void ResetDisplayInfos(); - - vgui::HFont m_hFont; - NeoUI::Context m_uiCtx; - - wchar_t m_wszKillerTitle[256] = {}; - wchar_t m_wszBindBtnMsg[128] = {}; - - wchar_t m_wszDmgerNamesList[MAX_PLAYERS][MAX_PLAYER_NAME_LENGTH + 1] = {}; - AttackersTotals m_attackersTotals[MAX_PLAYERS] = {}; - int m_iClassIdxsList[MAX_PLAYERS] = {}; - int m_iTeamIdxsList[MAX_PLAYERS] = {}; - int m_iEntIdxsList[MAX_PLAYERS] = {}; - int m_iAttackersTotalsSize = 0; - -private: - CPanelAnimationVarAliasType(int, xpos, "xpos", "20", "proportional_xpos"); - CPanelAnimationVarAliasType(int, ypos, "ypos", "150", "proportional_ypos"); - CPanelAnimationVar(Color, m_boxColor, "box_color", "200 200 200 40"); -}; diff --git a/src/game/client/neo/ui/neo_hud_killer_info.cpp b/src/game/client/neo/ui/neo_hud_killer_info.cpp new file mode 100644 index 0000000000..62e4828495 --- /dev/null +++ b/src/game/client/neo/ui/neo_hud_killer_info.cpp @@ -0,0 +1,208 @@ +#include +#include "neo_hud_killer_info.h" + +#include "iclientmode.h" + +#include "c_neo_killer_infos.h" +#include "c_neo_player.h" +#include "neo_gamerules.h" +#include "vgui/ILocalize.h" +#include "inputsystem/iinputsystem.h" +#include "IGameUIFuncs.h" +#include "igamesystem.h" +#include "cdll_client_int.h" +#include "neo_scoreboard.h" +#include + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +DECLARE_NAMED_HUDELEMENT(CNEOHud_KillerInfo, neo_killer_damage_info) + +NEO_HUD_ELEMENT_DECLARE_FREQ_CVAR(KillerInfo, 0.25) + +CNEOHud_KillerInfo::CNEOHud_KillerInfo(const char *pszName, vgui::Panel *parent) + : CHudElement(pszName) + , vgui::Panel(parent, pszName) +{ + SetAutoDelete(true); + if (parent) + { + SetParent(parent); + } + else + { + SetParent(g_pClientMode->GetViewport()); + } + SetVisible(false); + CommonSetupHUD(); +} + +void CNEOHud_KillerInfo::ApplySchemeSettings(vgui::IScheme *pScheme) +{ + vgui::Panel::ApplySchemeSettings(pScheme); + m_hFontNormal = pScheme->GetFont("HudSelectionText", true); + m_hFontTitle = pScheme->GetFont("NeoUINormal", true); + CommonSetupHUD(); +} + +void CNEOHud_KillerInfo::CommonSetupHUD() +{ + int wide, tall; + vgui::surface()->GetScreenSize(wide, tall); + SetBounds(0, 0, wide, tall); + + SetFgColor(COLOR_TRANSPARENT); + SetBgColor(COLOR_TRANSPARENT); +} + +void CNEOHud_KillerInfo::resetHUDState() +{ + V_memset(&g_neoKillerInfos, 0, sizeof(CNEOKillerInfos)); +} + +void CNEOHud_KillerInfo::UpdateStateForNeoHudElementDraw() +{ + const bool bIsPreRoundFreeze = (NEORules()->GetRoundStatus() == NeoRoundStatus::PreRoundFreeze); + if (bIsPreRoundFreeze && false == m_preRoundFreezeCleared) + { + resetHUDState(); + } + m_preRoundFreezeCleared = bIsPreRoundFreeze; + + // If to show or not + const C_NEO_Player *pLocalPlayer = C_NEO_Player::GetLocalNEOPlayer(); + m_bPlayerShownHud = pLocalPlayer + && (const_cast(pLocalPlayer)->IsPlayerDead() && g_neoKillerInfos.bHasDmgInfos) + && (pLocalPlayer->GetTeamNumber() == TEAM_JINRAI + || pLocalPlayer->GetTeamNumber() == TEAM_NSF) + && (gpGlobals->curtime < (const_cast(pLocalPlayer)->GetDeathTime() + DEATH_ANIMATION_TIME)); + + const CSteamID steamID = GetSteamIDForPlayerIndex(g_neoKillerInfos.iEntIndex); + if (steamID.IsValid()) + { + m_avatar.SetAvatarSteamID(steamID, k_EAvatarSize184x184); + } + else + { + m_avatar.ClearAvatarSteamID(); + } +} + +void CNEOHud_KillerInfo::DrawNeoHudElement() +{ + const C_NEO_Player *pLocalPlayer = C_NEO_Player::GetLocalNEOPlayer(); + if (!ShouldDraw() || !m_bPlayerShownHud || !pLocalPlayer) + { + return; + } + + const int iKillerEntIndex = g_neoKillerInfos.iEntIndex; + const bool bHasKiller = iKillerEntIndex > 0 || iKillerEntIndex == NEO_ENVIRON_KILLED; + if (!bHasKiller) + { + return; + } + + int wide, tall; + vgui::surface()->GetScreenSize(wide, tall); + + const bool bSuicideKill = (iKillerEntIndex == pLocalPlayer->entindex()) || + (iKillerEntIndex == NEO_ENVIRON_KILLED); + + CAvatarImage *pAvatarImg = (CNEOScoreBoard::ShowAvatars() && m_avatar.IsValid()) + ? &m_avatar : nullptr; + + // 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; + + wchar_t wszText[256] = {}; + + // Killer title + if (bSuicideKill) + { + // Show a suicide/self kill if it matches the local player's entity index + V_wcscpy_safe(wszText, L"YOU KILLED YOURSELF"); + } + else + { + V_swprintf_safe(wszText, L"Killed by %ls", g_neoKillerInfos.wszKillerName); + } + + // Make sure box at least fits the texts + int iTitleWide = 0; + int iTitleTall = 0; + [[maybe_unused]] int iNormalWide = 0; + int iNormalTall = 0; + vgui::surface()->GetTextSize(m_hFontTitle, wszText, iTitleWide, iTitleTall); + vgui::surface()->GetTextSize(m_hFontNormal, L"A", iNormalWide, iNormalTall); + + const int iBoxWide = Max(wide / 4, iTextOffsetX + iTitleWide + iMargin); + const int iBoxHeight = (pAvatarImg) + ? (2 * iMargin) + iAvatarWT + : iMargin + iTitleTall + iNormalTall + iMargin; + + const vgui::IntRect rect = { + .x0 = (wide / 2) - (iBoxWide / 2), + .y0 = static_cast(tall * 0.66f) - (iBoxHeight / 2), + .x1 = (wide / 2) + (iBoxWide / 2), + .y1 = static_cast(tall * 0.66f) + (iBoxHeight / 2), + }; + DrawNeoHudRoundedBox(rect.x0, rect.y0, rect.x1, rect.y1, COLOR_BLACK_TRANSPARENT); + + if (pAvatarImg) + { + pAvatarImg->SetPos(rect.x0 + iMargin, rect.y0 + iMargin); + pAvatarImg->SetSize(iAvatarWT, iAvatarWT); + pAvatarImg->Paint(); + } + + vgui::surface()->DrawSetTextFont(m_hFontTitle); + vgui::surface()->DrawSetTextColor(COLOR_NEO_WHITE); + vgui::surface()->DrawSetTextPos( + rect.x0 + iTextOffsetX, + rect.y0 + iMargin); + vgui::surface()->DrawPrintText(wszText, V_wcslen(wszText)); + + // Killer info + if (bSuicideKill) + { + // Just state the weapon, the rest of the infos aren't useful + // If it's an empty weapon, it's generally a suicide command kill + if (g_neoKillerInfos.wszKilledWith[0] == L'\0') + { + V_wcscpy_safe(wszText, (iKillerEntIndex == NEO_ENVIRON_KILLED) + ? L"Killed by map" + : L"Killed by map or used suicide command"); + } + else + { + V_swprintf_safe(wszText, L"Killed by %ls", g_neoKillerInfos.wszKilledWith); + } + } + else + { + V_swprintf_safe(wszText, L"%dhp %ls at %.2fm with %ls" + , g_neoKillerInfos.iHP + , GetNeoClassNameW(g_neoKillerInfos.iClass) + , g_neoKillerInfos.flDistance + , g_neoKillerInfos.wszKilledWith); + } + + vgui::surface()->DrawSetTextFont(m_hFontNormal); + vgui::surface()->DrawSetTextPos( + rect.x0 + iTextOffsetX, + rect.y0 + iMargin + iTitleTall); + vgui::surface()->DrawPrintText(wszText, V_wcslen(wszText)); +} + +void CNEOHud_KillerInfo::Paint() +{ + SetFgColor(COLOR_TRANSPARENT); + SetBgColor(COLOR_TRANSPARENT); + vgui::Panel::Paint(); + PaintNeoElement(); +} + diff --git a/src/game/client/neo/ui/neo_hud_killer_info.h b/src/game/client/neo/ui/neo_hud_killer_info.h new file mode 100644 index 0000000000..50f440186e --- /dev/null +++ b/src/game/client/neo/ui/neo_hud_killer_info.h @@ -0,0 +1,34 @@ +#pragma once + +#include "neo_hud_childelement.h" +#include "neo_player_shared.h" + +#include +#include +#include + +class CNEOHud_KillerInfo : public CNEOHud_ChildElement, public CHudElement, public vgui::Panel +{ + DECLARE_CLASS_SIMPLE(CNEOHud_KillerInfo, CNEOHud_ChildElement) + +public: + CNEOHud_KillerInfo(const char *pszName, vgui::Panel *parent = nullptr); + + void ApplySchemeSettings(vgui::IScheme *pScheme) override; + void Paint() override; + void resetHUDState() override; + +protected: + void UpdateStateForNeoHudElementDraw() override; + void DrawNeoHudElement() override; + ConVar *GetUpdateFrequencyConVar() const override; + +private: + void CommonSetupHUD(); + + vgui::HFont m_hFontNormal; + vgui::HFont m_hFontTitle; + CAvatarImage m_avatar; + bool m_preRoundFreezeCleared = false; + bool m_bPlayerShownHud = false; +}; diff --git a/src/game/client/neo/ui/neo_root_settings.cpp b/src/game/client/neo/ui/neo_root_settings.cpp index b53d5eeb5e..d5a0e17f17 100644 --- a/src/game/client/neo/ui/neo_root_settings.cpp +++ b/src/game/client/neo/ui/neo_root_settings.cpp @@ -12,7 +12,6 @@ #include #include #include "vgui/ISystem.h" -#include "neo_hud_killer_damage_info.h" #include "voice_status.h" #include "neo_ui.h" @@ -689,7 +688,6 @@ void NeoSettingsRestore(NeoSettings *ns, const NeoSettings::Keys::Flags flagsKey pHUD->iShowFps = cvr->cl_showfps.GetInt(); pHUD->bEnableRangeFinder = cvr->cl_neo_hud_rangefinder_enabled.GetBool(); pHUD->iExtendedKillfeed = cvr->cl_neo_hud_extended_killfeed.GetInt(); - pHUD->iKdinfoToggletype = cvr->cl_neo_kdinfo_toggletype.GetInt(); pHUD->iScoreboardPadding = cvr->cl_neo_hud_scoreboard_padding.GetInt(); pHUD->bShowHudContextHints = cvr->cl_neo_hud_context_hint_enabled.GetBool(); pHUD->bShowHudContextHintPlayerTakeover = cvr->cl_neo_hud_context_hint_show_player_takeover_hint.GetBool(); @@ -957,7 +955,6 @@ void NeoSettingsSave(const NeoSettings *ns) cvr->cl_showfps.SetValue(pHUD->iShowFps); cvr->cl_neo_hud_rangefinder_enabled.SetValue(pHUD->bEnableRangeFinder); cvr->cl_neo_hud_extended_killfeed.SetValue(pHUD->iExtendedKillfeed); - cvr->cl_neo_kdinfo_toggletype.SetValue(pHUD->iKdinfoToggletype); cvr->cl_neo_hud_scoreboard_padding.SetValue(pHUD->iScoreboardPadding); cvr->cl_neo_hud_context_hint_enabled.SetValue(pHUD->bShowHudContextHints); cvr->cl_neo_hud_context_hint_show_player_takeover_hint.SetValue(pHUD->bShowHudContextHintPlayerTakeover); @@ -1061,12 +1058,6 @@ static const wchar_t* OBJVERBOSITY_LABELS[] = { L"Minimal", L"Full" }; static const wchar_t *SHOWFPS_LABELS[] = { L"Disabled", L"Enabled (FPS)", L"Enabled (Smooth FPS)", }; -static const wchar_t *KDMGINFO_TOGGLETYPE_LABELS[KDMGINFO_TOGGLETYPE__TOTAL] = { - L"Always", // KDMGINFO_TOGGLETYPE_ROUND - L"Reset per match", // KDMGINFO_TOGGLETYPE_MATCH - L"Never", // KDMGINFO_TOGGLETYPE_NEVER -}; - static const wchar_t *EQUIP_UTILITY_PRIORITY_LABELS[NeoSettings::EquipUtilityPriorityType::EQUIP_UTILITY_PRIORITY__TOTAL] = { L"Frag, Smoke, Detpack", // EQUIP_UTILITY_PRIORITY_FRAG_SMOKE_DETPACK L"Class Specific First" // EQUIP_UTILITY_PRIORITY_CLASS_SPECIFIC @@ -1684,7 +1675,6 @@ void NeoSettings_HUD(NeoSettings *ns) NeoUI::RingBox(L"Show FPS", SHOWFPS_LABELS, ARRAYSIZE(SHOWFPS_LABELS), &pHud->iShowFps); NeoUI::RingBoxBool(L"Show rangefinder", &pHud->bEnableRangeFinder); NeoUI::RingBox(L"Extended killfeed", EXT_KILLFEED_LABELS, ARRAYSIZE(EXT_KILLFEED_LABELS), &pHud->iExtendedKillfeed); - NeoUI::RingBox(L"Killer damage info auto show", KDMGINFO_TOGGLETYPE_LABELS, KDMGINFO_TOGGLETYPE__TOTAL, &pHud->iKdinfoToggletype); NeoUI::RingBox(L"Scoreboard padding", NEOSCOREBOARDPADDING_LABELS, NEOSCOREBOARDPADDING__TOTAL, &pHud->iScoreboardPadding); NeoUI::Divider(L"Contextual hints"); diff --git a/src/game/client/neo/ui/neo_root_settings.h b/src/game/client/neo/ui/neo_root_settings.h index 230a8573b2..6065ce8cc0 100644 --- a/src/game/client/neo/ui/neo_root_settings.h +++ b/src/game/client/neo/ui/neo_root_settings.h @@ -211,7 +211,6 @@ struct NeoSettings bool bShowHudContextHintBotInteract; bool bShowHudContextHighlightObject; bool bShowHudContextHighlightPlayer; - int iKdinfoToggletype; int iScoreboardPadding; // IFF Markers @@ -275,7 +274,6 @@ struct NeoSettings CONVARREF_DEF(cl_neo_streamermode_autodetect_obs); CONVARREF_DEF(cl_neo_hud_rangefinder_enabled); CONVARREF_DEF(sv_unlockedchapters); - CONVARREF_DEF(cl_neo_kdinfo_toggletype); CONVARREF_DEF(cl_neo_hud_context_hint_enabled); CONVARREF_DEF(cl_neo_hud_context_hint_show_player_takeover_hint); CONVARREF_DEF(cl_neo_hud_context_hint_show_object_interact_hint); diff --git a/src/game/client/neo/ui/neo_scoreboard.cpp b/src/game/client/neo/ui/neo_scoreboard.cpp index ad104b9558..ad816c7b8c 100644 --- a/src/game/client/neo/ui/neo_scoreboard.cpp +++ b/src/game/client/neo/ui/neo_scoreboard.cpp @@ -36,8 +36,10 @@ enum EColsPlayers COLSPLAYERS_PING = 0, COLSPLAYERS_AVATAR, COLSPLAYERS_NAME, - COLSPLAYERS_READYUP, - COLSPLAYERS_CLASS, + COLSPLAYERS_READYUP, // Hidden apart from ready-up + COLSPLAYERS_DMG_DEALT, // Hidden apart from playing local player's death + COLSPLAYERS_DMG_TAKEN, // Hidden apart from playing local player's death + COLSPLAYERS_CLASS, // Hidden if team play, enemy team, and local player alive COLSPLAYERS_RANK, COLSPLAYERS_XP, COLSPLAYERS_DEATH, @@ -100,6 +102,11 @@ CNEOScoreBoard::~CNEOScoreBoard() } } +bool CNEOScoreBoard::ShowAvatars() +{ + return neo_show_scoreboard_avatars.GetBool() && !cl_neo_streamermode.GetBool(); +} + const char *CNEOScoreBoard::GetName() { return PANEL_SCOREBOARD; @@ -202,7 +209,7 @@ void CNEOScoreBoard::ApplySchemeSettings(vgui::IScheme *pScheme) SetBgColor(COLOR_TRANSPARENT); static constexpr const char *FONT_NAMES[NeoUI::FONT__TOTAL] = { - "HudSelectionText", //"NeoUIScoreboard", + "HudSelectionText", "NHudOCR", "NHudOCRSmallNoAdditive", "ClientTitleFont", @@ -369,11 +376,42 @@ void CNEOScoreBoard::Update() pPlayerInfo->iXP = bIsPlaying ? g_PR->GetXP(i) : 0; pPlayerInfo->iDeaths = bIsPlaying ? g_PR->GetDeaths(i) : 0; + // Damage infos + const bool bShowDamageInfo = pLocalPlayer->IsPlayerDead() + && NEORules()->InRoundState() + && (pLocalPlayer->GetTeamNumber() == TEAM_JINRAI + || pLocalPlayer->GetTeamNumber() == TEAM_NSF) + && bIsTeamplay; + + if (bShowDamageInfo && pNeoPlayer && false == pNeoPlayer->IsHLTV() + && (pNeoPlayer->GetTeamNumber() == TEAM_JINRAI + || pNeoPlayer->GetTeamNumber() == TEAM_NSF)) + { + pPlayerInfo->iDealtDmgs = pNeoPlayer->GetAttackersScores(pLocalPlayer->entindex()); + pPlayerInfo->iDealtHits = pNeoPlayer->GetAttackerHits(pLocalPlayer->entindex()); + pPlayerInfo->iTakenDmgs = pLocalPlayer->GetAttackersScores(pNeoPlayer->entindex()); + pPlayerInfo->iTakenHits = pLocalPlayer->GetAttackerHits(pNeoPlayer->entindex()); + pPlayerInfo->bKilledYou = (pNeoPlayer->entindex() == g_neoKillerInfos.iEntIndex); + pPlayerInfo->bYouKilled = pLocalPlayer->m_rfNeoPlayerIdxsKilledByLocal[pNeoPlayer->entindex()]; + } + else + { + pPlayerInfo->iDealtDmgs = 0; + pPlayerInfo->iDealtHits = 0; + pPlayerInfo->iTakenDmgs = 0; + pPlayerInfo->iTakenHits = 0; + pPlayerInfo->bKilledYou = false; + pPlayerInfo->bYouKilled = false; + } + // pPlayerInfo->iClass const bool bShowClass = (false == bLocalPlaying) // Spectating || (false == bIsTeamplay && g_PR->IsLocalPlayer(i)) // DM - Only see own class - || (bIsTeamplay && iLocalPlayerTeam == pPlayerInfo->iTeam); // Team - See own team's classes + || (bIsTeamplay && iLocalPlayerTeam == pPlayerInfo->iTeam) // Team - See own team's classes + || (bShowDamageInfo + && ((pPlayerInfo->iDealtDmgs > 0 && pPlayerInfo->iDealtHits > 0) + || (pPlayerInfo->iTakenDmgs > 0 && pPlayerInfo->iTakenHits > 0))); // Dead and have damage dealt/taken if (bShowClass) { pPlayerInfo->iClass = (bIsImpersonating) @@ -494,14 +532,11 @@ void CNEOScoreBoard::Update() m_flNextUpdateTime = gpGlobals->curtime + 1.0f; } -bool CNEOScoreBoard::ShowAvatars() -{ - return neo_show_scoreboard_avatars.GetBool() && !cl_neo_streamermode.GetBool(); -} - void CNEOScoreBoard::OnMainLoop(const NeoUI::Mode eMode) { - if (!NEORules()) + C_NEO_Player *pLocalPlayer = C_NEO_Player::GetLocalNEOPlayer(); + + if (!NEORules() || !pLocalPlayer) { return; } @@ -527,8 +562,15 @@ void CNEOScoreBoard::OnMainLoop(const NeoUI::Mode eMode) const int iPopupCardPerRowTallButtons = iPopupCardRowTallBase * 2.25f; const int iAvatarOffset = m_uiCtx.iMarginX; const int iAvatarWT = ShowAvatars() ? (iPopupCardPerRowTallAvatarName - (iAvatarOffset * 2)) : 0; + const bool bIsTeamplay = NEORules()->IsTeamplay(); const bool bShowReadyUp = sv_neo_readyup_lobby.GetBool() && NEORules()->m_nRoundStatus == NeoRoundStatus::Idle; + const bool bShowDamageInfo = pLocalPlayer->IsPlayerDead() + && NEORules()->InRoundState() + && g_neoKillerInfos.bHasDmgInfos + && bIsTeamplay + && (pLocalPlayer->GetTeamNumber() == TEAM_JINRAI + || pLocalPlayer->GetTeamNumber() == TEAM_NSF); static CrosshairInfo staticXhairInfo = {}; bool bHasRanklessDog = false; @@ -551,11 +593,9 @@ void CNEOScoreBoard::OnMainLoop(const NeoUI::Mode eMode) } const int iGap = m_uiCtx.iMarginX; - const bool bIsTeamplay = NEORules()->IsTeamplay(); const int iMaxSidePlayers = (bIsTeamplay) ? Max(iaTeamTally[TEAM_JINRAI], iaTeamTally[TEAM_NSF]) : Ceil2Int((iaTeamTally[TEAM_JINRAI] + iaTeamTally[TEAM_NSF]) / 2.0f); - C_NEO_Player *pLocalPlayer = C_NEO_Player::GetLocalNEOPlayer(); const int iLocalUserID = pLocalPlayer->GetUserID(); const int iLocalPlayerTeam = pLocalPlayer->GetTeamNumber(); @@ -625,15 +665,19 @@ void CNEOScoreBoard::OnMainLoop(const NeoUI::Mode eMode) m_uiCtx.colors.sectionBg = COLOR_BLACK_TRANSPARENT; + const int iClassWide = NeoUI::SuitableWideByWStr(L"Support", NeoUI::SUITABLEWIDE_TABLE); + int iColsWidePlayersList[COLSPLAYERS__TOTAL] = {}; iColsWidePlayersList[COLSPLAYERS_PING] = NeoUI::SuitableWideByWStr(L"BOT", NeoUI::SUITABLEWIDE_TABLE); iColsWidePlayersList[COLSPLAYERS_AVATAR] = m_uiCtx.layout.iRowTall; iColsWidePlayersList[COLSPLAYERS_NAME] = 0; iColsWidePlayersList[COLSPLAYERS_READYUP] = bShowReadyUp ? NeoUI::SuitableWideByWStr(L"NOT READY", NeoUI::SUITABLEWIDE_TABLE) : 0; - iColsWidePlayersList[COLSPLAYERS_CLASS] = NeoUI::SuitableWideByWStr(L"Support", NeoUI::SUITABLEWIDE_TABLE); + iColsWidePlayersList[COLSPLAYERS_DMG_DEALT] = bShowDamageInfo ? NeoUI::SuitableWideByWStr(L"200 in 99", NeoUI::SUITABLEWIDE_TABLE) : 0; + iColsWidePlayersList[COLSPLAYERS_DMG_TAKEN] = bShowDamageInfo ? NeoUI::SuitableWideByWStr(L"200 in 99", NeoUI::SUITABLEWIDE_TABLE) : 0; + iColsWidePlayersList[COLSPLAYERS_CLASS] = iClassWide; iColsWidePlayersList[COLSPLAYERS_RANK] = NeoUI::SuitableWideByWStr(bHasRanklessDog ? L"Rankless Dog" : L"Lieutenant", NeoUI::SUITABLEWIDE_TABLE); iColsWidePlayersList[COLSPLAYERS_XP] = NeoUI::SuitableWideByWStr(L"-99", NeoUI::SUITABLEWIDE_TABLE); - iColsWidePlayersList[COLSPLAYERS_DEATH] = NeoUI::SuitableWideByWStr(L"Deaths", NeoUI::SUITABLEWIDE_TABLE); + iColsWidePlayersList[COLSPLAYERS_DEATH] = NeoUI::SuitableWideByWStr(L"D#", NeoUI::SUITABLEWIDE_TABLE); int iColsWidePlayersTotalNonName = 0; for (int i = 0; i < COLSPLAYERS__TOTAL; ++i) { @@ -666,6 +710,11 @@ void CNEOScoreBoard::OnMainLoop(const NeoUI::Mode eMode) } const bool bNSFFirst = cl_neo_hud_team_swap_sides.GetBool() && TEAM_NSF == iLocalPlayerTeam; + const bool bShowClass = (iLocalPlayerTeam <= TEAM_SPECTATOR) + || false == bIsTeamplay + || bShowDamageInfo + || iLocalPlayerTeam == iCurTeam; + iColsWidePlayersList[COLSPLAYERS_CLASS] = bShowClass ? iClassWide : 0; m_uiCtx.dPanel.x = (wide / 2) - (iRootSubPanelWide / 2); if ((bNSFFirst && TEAM_JINRAI == iCurTeam) @@ -686,7 +735,7 @@ void CNEOScoreBoard::OnMainLoop(const NeoUI::Mode eMode) // One for the heading m_uiCtx.dPanel.tall = m_uiCtx.layout.iRowTall * (1 + ((iCurTeam >= FIRST_GAME_TEAM) ? iMaxSidePlayers : iaTeamTally[iCurTeam])); - iColsWidePlayersList[COLSPLAYERS_NAME] = m_uiCtx.dPanel.wide - iColsWidePlayersTotalNonName; + iColsWidePlayersList[COLSPLAYERS_NAME] = m_uiCtx.dPanel.wide - iColsWidePlayersTotalNonName + (bShowClass ? 0 : iClassWide); iColsWideNonPlayersList[COLSNONPLAYERS_NAME] = m_uiCtx.dPanel.wide - iColsWideNonPlayersTotalNonName; NeoUI::BeginSection(NeoUI::SECTIONFLAG_DISABLEOFFSETS); @@ -767,11 +816,17 @@ void CNEOScoreBoard::OnMainLoop(const NeoUI::Mode eMode) NeoUI::Pad(); // Name column if (iCurTeam >= FIRST_GAME_TEAM) { - NeoUI::Label(L"Ready"); // Hidden when not used + const Color bkupNormalFg = m_uiCtx.colors.normalFg; + NeoUI::Label(L"Ready"); // Hidden when not used/not in ready-up + m_uiCtx.colors.normalFg = COLOR_GREEN; + NeoUI::Label(L"Dmg \u25B2"); // Dealt - Hidden when not showing damage info + m_uiCtx.colors.normalFg = COLOR_RED; + NeoUI::Label(L"Dmg \u25BC"); // Taken - Hidden when not showing damage info + m_uiCtx.colors.normalFg = bkupNormalFg; NeoUI::Label(L"Class"); NeoUI::Label(L"Rank"); NeoUI::Label(L"XP"); - NeoUI::Label(L"Deaths"); + NeoUI::Label(L"D"); } if (NeoUI::MODE_PAINT == m_uiCtx.eMode) @@ -964,6 +1019,39 @@ void CNEOScoreBoard::OnMainLoop(const NeoUI::Mode eMode) // Ready-up (Hidden when not used) NeoUI::Label(pPlayerInfo->bReady ? L"READY" : L"NOT READY"); + // Damage infos (Hidden when not used) + // * Dealt + const Color bkupNormalFg = m_uiCtx.colors.normalFg; + if (bShowDamageInfo && pPlayerInfo->iDealtDmgs > 0 && pPlayerInfo->iDealtHits > 0) + { + if (pPlayerInfo->bYouKilled) + { + m_uiCtx.colors.normalFg = COLOR_RED; + } + V_swprintf_safe(wszText, L"%d in %d", pPlayerInfo->iDealtDmgs, pPlayerInfo->iDealtHits); + NeoUI::Label(wszText); + m_uiCtx.colors.normalFg = bkupNormalFg; + } + else + { + NeoUI::Pad(); + } + // * Taken + if (bShowDamageInfo && pPlayerInfo->iTakenDmgs > 0 && pPlayerInfo->iTakenHits > 0) + { + if (pPlayerInfo->bKilledYou) + { + m_uiCtx.colors.normalFg = COLOR_RED; + } + V_swprintf_safe(wszText, L"%d in %d", pPlayerInfo->iTakenDmgs, pPlayerInfo->iTakenHits); + NeoUI::Label(wszText); + m_uiCtx.colors.normalFg = bkupNormalFg; + } + else + { + NeoUI::Pad(); + } + // Class NeoUI::Label(GetNeoClassNameW(pPlayerInfo->iClass)); @@ -1072,16 +1160,16 @@ void CNEOScoreBoard::OnMainLoop(const NeoUI::Mode eMode) if (SteamFriends()) { CSteamID &steamID = m_playerPopup.steamID; + const char *pszOverlay = nullptr; if (NeoUI::ButtonTexture("vgui/hud/player_profile", "GAME", L"Profile").bPressed) { - SteamFriends()->ActivateGameOverlayToUser("steamid", steamID); - NeoUI::ClosePopup(); + pszOverlay = "steamid"; } + if (m_playerPopup.iUserID != iLocalUserID) { - const char *pszOverlay = nullptr; if (NeoUI::ButtonTexture("vgui/hud/player_message", "GAME", L"Message").bPressed) { @@ -1121,12 +1209,12 @@ void CNEOScoreBoard::OnMainLoop(const NeoUI::Mode eMode) default: break; } + } - if (pszOverlay) - { - SteamFriends()->ActivateGameOverlayToUser(pszOverlay, steamID); - NeoUI::ClosePopup(); - } + if (pszOverlay) + { + SteamFriends()->ActivateGameOverlayToUser(pszOverlay, steamID); + NeoUI::ClosePopup(); } } diff --git a/src/game/client/neo/ui/neo_scoreboard.h b/src/game/client/neo/ui/neo_scoreboard.h index ecaabb185f..e6cbcfb275 100644 --- a/src/game/client/neo/ui/neo_scoreboard.h +++ b/src/game/client/neo/ui/neo_scoreboard.h @@ -27,21 +27,32 @@ struct MapAvatarValue struct CNEOScoreBoardPlayer { + // Common section int iUserID; // More reliable than player index int iTeam; - int iDeaths; - int iXP; - int iClass; int iPing; bool bDead; bool bBot; bool bMuted; - bool bReady; CSteamID steamID; MapAvatarValue avatar; wchar_t wszName[MAX_PLAYER_NAME_LENGTH]; wchar_t wszClantag[NEO_MAX_CLANTAG_LENGTH]; char szCrosshair[NEO_XHAIR_SEQMAX]; + + // Normal scoreboard section + int iDeaths; + int iXP; + int iClass; + bool bReady; + + // Damage info + int iDealtDmgs; + int iDealtHits; + int iTakenDmgs; + int iTakenHits; + bool bKilledYou; + bool bYouKilled; }; class CNEOScoreBoard : public vgui::Panel, public IViewPortPanel, public CGameEventListener @@ -52,6 +63,8 @@ class CNEOScoreBoard : public vgui::Panel, public IViewPortPanel, public CGameEv CNEOScoreBoard(IViewPort *pViewPort); ~CNEOScoreBoard(); + static bool ShowAvatars(); + const char *GetName() final; void ApplySchemeSettings(vgui::IScheme *pScheme) final; void Paint() final; @@ -76,8 +89,6 @@ class CNEOScoreBoard : public vgui::Panel, public IViewPortPanel, public CGameEv void SetParent(vgui::VPANEL parent) final { BaseClass::SetParent( parent ); } void FireGameEvent(IGameEvent *event) final; - bool ShowAvatars(); - void OnMainLoop(const NeoUI::Mode eMode); MESSAGE_FUNC_INT(OnPollHideCode, "PollHideCode", code); @@ -87,7 +98,7 @@ class CNEOScoreBoard : public vgui::Panel, public IViewPortPanel, public CGameEv int m_HLTVSpectators = 0; int m_iTotalPlayers = 0; - CNEOScoreBoardPlayer m_playersInfo[MAX_PLAYERS] = {}; + CNEOScoreBoardPlayer m_playersInfo[MAX_PLAYERS + 1] = {}; CNEOScoreBoardPlayer m_playerPopup = {}; wchar_t m_wszHostname[128] = {}; diff --git a/src/game/client/neo/ui/neo_ui.h b/src/game/client/neo/ui/neo_ui.h index b4bd7bf869..f7dd16769f 100644 --- a/src/game/client/neo/ui/neo_ui.h +++ b/src/game/client/neo/ui/neo_ui.h @@ -535,6 +535,7 @@ void EndWidget(const CurrentWidgetState &wdgState); void SetPerRowLayout(const int iColTotal, const int *iColProportions = nullptr, const int iRowHeight = -1); // Layout a vertical within the (horizontal) column, iRowTotal = 0 to disable +// NEO TODO (nullsystem): Remove Vert layouting? void SetPerCellVertLayout(const int iRowTotal, const int *iRowProportions = nullptr); void BeginOverridePanelWide(const int iOverridePanelWide); diff --git a/src/game/server/neo/neo_player.cpp b/src/game/server/neo/neo_player.cpp index c15f1103a7..75772f300b 100644 --- a/src/game/server/neo/neo_player.cpp +++ b/src/game/server/neo/neo_player.cpp @@ -2297,7 +2297,7 @@ void CNEO_Player::StartShowDmgStats(const CTakeDamageInfo *info) CSingleUserRecipientFilter filter(this); filter.MakeReliable(); - UserMessageBegin(filter, "DamageInfo"); + UserMessageBegin(filter, "KillerDamageInfo"); { short attackerIdx = 0; auto *neoAttacker = info ? ToNEOPlayer(info->GetAttacker()) : nullptr; diff --git a/src/game/shared/hl2/hl2_usermessages.cpp b/src/game/shared/hl2/hl2_usermessages.cpp index 331ba94bd8..a02951b3f3 100644 --- a/src/game/shared/hl2/hl2_usermessages.cpp +++ b/src/game/shared/hl2/hl2_usermessages.cpp @@ -48,7 +48,7 @@ void RegisterUserMessages( void ) usermessages->Register( "RoundResult", -1 ); #ifdef NEO - usermessages->Register( "DamageInfo", -1 ); + usermessages->Register( "KillerDamageInfo", -1 ); usermessages->Register( "IdleRespawnShowMenu", -1 ); usermessages->Register( "CSpectatorTakeoverPlayer", -1 ); usermessages->Register( "AchievementMark", -1 ); @@ -59,4 +59,4 @@ void RegisterUserMessages( void ) RegisterHapticMessages(); #endif RegisterScriptMessages(); -} \ No newline at end of file +} From f0ef3e77624772563cc8e49ca5804eab26f274d8 Mon Sep 17 00:00:00 2001 From: nullsystem <15316579+nullsystem@users.noreply.github.com> Date: Sun, 12 Jul 2026 02:25:21 +0100 Subject: [PATCH 2/2] Change to from/to, drop coloring of header --- src/game/client/neo/ui/neo_scoreboard.cpp | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/game/client/neo/ui/neo_scoreboard.cpp b/src/game/client/neo/ui/neo_scoreboard.cpp index ad816c7b8c..b9ac0d98b5 100644 --- a/src/game/client/neo/ui/neo_scoreboard.cpp +++ b/src/game/client/neo/ui/neo_scoreboard.cpp @@ -672,8 +672,8 @@ void CNEOScoreBoard::OnMainLoop(const NeoUI::Mode eMode) iColsWidePlayersList[COLSPLAYERS_AVATAR] = m_uiCtx.layout.iRowTall; iColsWidePlayersList[COLSPLAYERS_NAME] = 0; iColsWidePlayersList[COLSPLAYERS_READYUP] = bShowReadyUp ? NeoUI::SuitableWideByWStr(L"NOT READY", NeoUI::SUITABLEWIDE_TABLE) : 0; - iColsWidePlayersList[COLSPLAYERS_DMG_DEALT] = bShowDamageInfo ? NeoUI::SuitableWideByWStr(L"200 in 99", NeoUI::SUITABLEWIDE_TABLE) : 0; - iColsWidePlayersList[COLSPLAYERS_DMG_TAKEN] = bShowDamageInfo ? NeoUI::SuitableWideByWStr(L"200 in 99", NeoUI::SUITABLEWIDE_TABLE) : 0; + iColsWidePlayersList[COLSPLAYERS_DMG_DEALT] = bShowDamageInfo ? NeoUI::SuitableWideByWStr(L"Dmg from", NeoUI::SUITABLEWIDE_TABLE) : 0; + iColsWidePlayersList[COLSPLAYERS_DMG_TAKEN] = bShowDamageInfo ? NeoUI::SuitableWideByWStr(L"Dmg from", NeoUI::SUITABLEWIDE_TABLE) : 0; iColsWidePlayersList[COLSPLAYERS_CLASS] = iClassWide; iColsWidePlayersList[COLSPLAYERS_RANK] = NeoUI::SuitableWideByWStr(bHasRanklessDog ? L"Rankless Dog" : L"Lieutenant", NeoUI::SUITABLEWIDE_TABLE); iColsWidePlayersList[COLSPLAYERS_XP] = NeoUI::SuitableWideByWStr(L"-99", NeoUI::SUITABLEWIDE_TABLE); @@ -816,13 +816,9 @@ void CNEOScoreBoard::OnMainLoop(const NeoUI::Mode eMode) NeoUI::Pad(); // Name column if (iCurTeam >= FIRST_GAME_TEAM) { - const Color bkupNormalFg = m_uiCtx.colors.normalFg; NeoUI::Label(L"Ready"); // Hidden when not used/not in ready-up - m_uiCtx.colors.normalFg = COLOR_GREEN; - NeoUI::Label(L"Dmg \u25B2"); // Dealt - Hidden when not showing damage info - m_uiCtx.colors.normalFg = COLOR_RED; - NeoUI::Label(L"Dmg \u25BC"); // Taken - Hidden when not showing damage info - m_uiCtx.colors.normalFg = bkupNormalFg; + NeoUI::Label(L"Dmg to"); // Dealt - Hidden when not showing damage info + NeoUI::Label(L"Dmg from"); // Taken - Hidden when not showing damage info NeoUI::Label(L"Class"); NeoUI::Label(L"Rank"); NeoUI::Label(L"XP");