diff --git a/game/neo/scripts/HudLayout.res b/game/neo/scripts/HudLayout.res index eff196c6d..d692ff1c0 100644 --- a/game/neo/scripts/HudLayout.res +++ b/game/neo/scripts/HudLayout.res @@ -998,14 +998,9 @@ "tall" "200" } - neo_killer_damage_info + neo_killer_info { - "fieldName" "neo_killer_damage_info" - "xpos" "20" - "ypos" "150" - "wide" "640" - "tall" "480" - "box_color" "150 150 150 60" + "fieldName" "neo_killer_info" } neo_context_hint diff --git a/src/game/client/CMakeLists.txt b/src/game/client/CMakeLists.txt index b5c6c9002..9273ef749 100644 --- a/src/game/client/CMakeLists.txt +++ b/src/game/client/CMakeLists.txt @@ -1574,8 +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 - neo/c_neo_killer_infos.cpp + neo/c_neo_killer_damage_infos.cpp ) set_source_files_properties( @@ -1592,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_infos.h + neo/c_neo_killer_damage_infos.h ${UNITY_SOURCE_NEO_SRC_CLIENT} ) diff --git a/src/game/client/neo/neo_killer_damage_info.cpp b/src/game/client/neo/c_neo_killer_damage_infos.cpp similarity index 52% rename from src/game/client/neo/neo_killer_damage_info.cpp rename to src/game/client/neo/c_neo_killer_damage_infos.cpp index 52f88ad6e..a8c183ce8 100644 --- a/src/game/client/neo/neo_killer_damage_info.cpp +++ b/src/game/client/neo/c_neo_killer_damage_infos.cpp @@ -1,4 +1,7 @@ +#include "c_neo_killer_damage_infos.h" + #include +#include "strtools.h" #include "c_neo_player.h" #include "neo_gamerules.h" @@ -8,6 +11,114 @@ // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" +static inline AttackersTotals gTotals; +static const char *BORDER = "==========================\n"; + +void NeoUserIDsLocalKilledClear() +{ + g_neoUserIDsLocalKilledSize = 0; + V_memset(g_neoUserIDsLocalKilled, 0, sizeof(g_neoUserIDsLocalKilled)); +} + +void NeoDamageReportClear() +{ + g_neoDamageReportSize = 0; + V_memset(g_neoDamageReport, 0, sizeof(g_neoDamageReport)); +} + +void NeoKillerInfoClear() +{ + V_memset(&g_neoKillerInfos, 0, sizeof(CNEOKillerInfos)); +} + +void NeoAllKDReportsClear() +{ + NeoDamageReportClear(); + NeoUserIDsLocalKilledClear(); + NeoKillerInfoClear(); +} + +static ENEOCompactMsgFlag ReadDamageReport(bf_read &msg) +{ + // Read (server side) per-player damage stats + const int iAtkSize = Min(msg.ReadByte(), MAX_PLAYERS); + const ENEOCompactMsgFlag flags = static_cast(msg.ReadByte()); + + for (int i = 0; i < iAtkSize; ++i) + { + AttackersTotals *pDmgReport = &g_neoDamageReport[g_neoDamageReportSize]; + pDmgReport->iUserID = msg.ReadLong(); + + if (flags & NEO_COMPACT_MSG_FLAG_DMGS) + { + pDmgReport->iDealtDmgs = static_cast(msg.ReadByte()); + pDmgReport->iTakenDmgs = static_cast(msg.ReadByte()); + } + else + { + pDmgReport->iDealtDmgs = msg.ReadShort(); + pDmgReport->iTakenDmgs = msg.ReadShort(); + } + + if (flags & NEO_COMPACT_MSG_FLAG_HITS) + { + pDmgReport->iDealtHits = static_cast(msg.ReadByte()); + pDmgReport->iTakenHits = static_cast(msg.ReadByte()); + } + else + { + pDmgReport->iDealtHits = msg.ReadShort(); + pDmgReport->iTakenHits = msg.ReadShort(); + } + + auto *neoAttacker = USERID2NEOPLAYER(pDmgReport->iUserID); + if (!neoAttacker || neoAttacker->IsHLTV()) + { + continue; + } + + const char *dmgerName = neoAttacker->GetNeoPlayerName(); + if (!dmgerName) + { + continue; + } + + const char *dmgerClass = GetNeoClassName(neoAttacker->GetClass()); + + char infoLine[128] = {}; + if (pDmgReport->iDealtDmgs > 0 && pDmgReport->iDealtHits > 0) + { + V_sprintf_safe(infoLine, "Damage dealt to %s [%s]: %d in %d hits\n", + dmgerName, dmgerClass, + pDmgReport->iDealtDmgs, pDmgReport->iDealtHits); + ConMsg("%s", infoLine); + gTotals.iDealtDmgs += pDmgReport->iDealtDmgs; + gTotals.iDealtHits += pDmgReport->iDealtHits; + } + if (pDmgReport->iTakenDmgs > 0 && pDmgReport->iTakenHits > 0) + { + V_sprintf_safe(infoLine, "Damage taken from %s [%s]: %d in %d hits\n", + dmgerName, dmgerClass, + pDmgReport->iTakenDmgs, pDmgReport->iTakenHits); + ConMsg("%s", infoLine); + gTotals.iTakenDmgs += pDmgReport->iTakenDmgs; + gTotals.iTakenHits += pDmgReport->iTakenHits; + } + + ++g_neoDamageReportSize; + } + + return flags; +} + +static void PrintTotalReport() +{ + ConMsg("Total damage dealt: %d in %d hits\nTotal damage received from players: %d in %d hits\n%s\n", + gTotals.iDealtDmgs, gTotals.iDealtHits, + gTotals.iTakenDmgs, gTotals.iTakenHits, + BORDER); +} + // Console + activation of damage info static void __MsgFunc_KillerDamageInfo(bf_read &msg) { @@ -23,17 +134,14 @@ static void __MsgFunc_KillerDamageInfo(bf_read &msg) // 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)); + NeoKillerInfoClear(); g_neoKillerInfos.bHasDmgInfos = true; if (killerIdx > 0 && killerIdx <= gpGlobals->maxClients) @@ -94,58 +202,22 @@ static void __MsgFunc_KillerDamageInfo(bf_read &msg) } 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; - } + NeoDamageReportClear(); + gTotals = {}; + const ENEOCompactMsgFlag flags = ReadDamageReport(msg); + if (!(flags & NEO_COMPACT_MSG_FLAG_EXTRA)) + { + PrintTotalReport(); } +} - 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); +static void __MsgFunc_KillerDamageInfoExtra(bf_read &msg) +{ + ReadDamageReport(msg); + PrintTotalReport(); } + USER_MESSAGE_REGISTER(KillerDamageInfo); +USER_MESSAGE_REGISTER(KillerDamageInfoExtra); + diff --git a/src/game/client/neo/c_neo_killer_infos.h b/src/game/client/neo/c_neo_killer_damage_infos.h similarity index 71% rename from src/game/client/neo/c_neo_killer_infos.h rename to src/game/client/neo/c_neo_killer_damage_infos.h index a017b2a49..cbabf40de 100644 --- a/src/game/client/neo/c_neo_killer_infos.h +++ b/src/game/client/neo/c_neo_killer_damage_infos.h @@ -1,6 +1,7 @@ #pragma once #include "shareddefs.h" +#include "neo_player_shared.h" static constexpr const int WEP_NAME_MAXSTRLEN = 32; @@ -16,9 +17,16 @@ struct CNEOKillerInfos }; void NeoUserIDsLocalKilledClear(); +void NeoDamageReportClear(); +void NeoKillerInfoClear(); + +void NeoAllKDReportsClear(); // Global instance of CNEOKillerInfos and local-player's kill record inline CNEOKillerInfos g_neoKillerInfos; inline int g_neoUserIDsLocalKilledSize; inline int g_neoUserIDsLocalKilled[MAX_PLAYERS_ARRAY_SAFE]; +inline int g_neoDamageReportSize; +inline AttackersTotals g_neoDamageReport[MAX_PLAYERS_ARRAY_SAFE]; + diff --git a/src/game/client/neo/c_neo_killer_infos.cpp b/src/game/client/neo/c_neo_killer_infos.cpp deleted file mode 100644 index 08ef6e39a..000000000 --- a/src/game/client/neo/c_neo_killer_infos.cpp +++ /dev/null @@ -1,10 +0,0 @@ -#include "c_neo_killer_infos.h" - -#include "strtools.h" - -void NeoUserIDsLocalKilledClear() -{ - g_neoUserIDsLocalKilledSize = 0; - V_memset(g_neoUserIDsLocalKilled, 0, sizeof(g_neoUserIDsLocalKilled)); -} - diff --git a/src/game/client/neo/c_neo_player.cpp b/src/game/client/neo/c_neo_player.cpp index 9c34034ff..0b6563fae 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_infos.h" +#include "c_neo_killer_damage_infos.h" #include #include @@ -98,9 +98,6 @@ IMPLEMENT_CLIENTCLASS_DT(C_NEO_Player, DT_NEO_Player, CNEO_Player) RecvPropTime(RECVINFO(m_flJumpLastTime)), RecvPropTime(RECVINFO(m_flNextPingTime)), - RecvPropArray(RecvPropInt(RECVINFO(m_rfAttackersScores[0])), m_rfAttackersScores), - RecvPropArray(RecvPropFloat(RECVINFO(m_rfAttackersAccumlator[0])), m_rfAttackersAccumlator), - RecvPropArray(RecvPropInt(RECVINFO(m_rfAttackersHits[0])), m_rfAttackersHits), RecvPropArray(RecvPropVector(RECVINFO(m_vLastPingByStar[0])), m_vLastPingByStar), RecvPropInt(RECVINFO(m_NeoFlags)), @@ -117,10 +114,6 @@ IMPLEMENT_CLIENTCLASS_DT(C_NEO_Player, DT_NEO_Player, CNEO_Player) END_RECV_TABLE() BEGIN_PREDICTION_DATA(C_NEO_Player) - DEFINE_PRED_ARRAY(m_rfAttackersScores, FIELD_INTEGER, MAX_PLAYERS_ARRAY_SAFE, FTYPEDESC_INSENDTABLE), - DEFINE_PRED_ARRAY(m_rfAttackersAccumlator, FIELD_FLOAT, MAX_PLAYERS_ARRAY_SAFE, FTYPEDESC_INSENDTABLE), - DEFINE_PRED_ARRAY(m_rfAttackersHits, FIELD_INTEGER, MAX_PLAYERS_ARRAY_SAFE, FTYPEDESC_INSENDTABLE), - DEFINE_PRED_FIELD_TOL(m_flCamoAuxLastTime, FIELD_FLOAT, FTYPEDESC_INSENDTABLE, TD_MSECTOLERANCE), DEFINE_PRED_FIELD(m_bInThermOpticCamo, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE), @@ -479,7 +472,7 @@ C_NEO_Player::C_NEO_Player() m_flTocFactor = 0.15f; memset(m_szNeoNameWDupeIdx, 0, sizeof(m_szNeoNameWDupeIdx)); - if (IsLocalPlayer()) NeoUserIDsLocalKilledClear(); + ClearLocalPlayerDmgReports(); m_szNameDupePos = 0; } @@ -573,15 +566,6 @@ void C_NEO_Player::CheckLeanButtons() } } -int C_NEO_Player::GetAttackersScores(const int attackerIdx) const -{ - if (NEORules()->GetGameType() == NEO_GAME_TYPE_DM || NEORules()->GetGameType() == NEO_GAME_TYPE_TDM) - { - return m_rfAttackersScores.Get(attackerIdx); - } - return m_rfAttackersScores.Get(attackerIdx); -} - const char *C_NEO_Player::GetNeoClantag() const { if (!sv_neo_clantag_allow.GetBool() || @@ -650,11 +634,6 @@ bool C_NEO_Player::ClientWantNeoName() const return m_bClientWantNeoName; } -int C_NEO_Player::GetAttackerHits(const int attackerIdx) const -{ - return m_rfAttackersHits.Get(attackerIdx); -} - ConVar cl_neo_hud_health_mode("cl_neo_hud_health_mode", "1", FCVAR_ARCHIVE, "Health display mode. 0 = Percent, 1 = Hit points, 2 = Effective hit points", true, 0, true, 2); @@ -1208,8 +1187,7 @@ void C_NEO_Player::PreThink( void ) CLocalPlayerFilter filter; enginesound->SetPlayerDSP(filter, 0, true); - NeoUserIDsLocalKilledClear(); - V_memset(&g_neoKillerInfos, 0, sizeof(CNEOKillerInfos)); + ClearLocalPlayerDmgReports(); // Reset the cache of other players crosshair data on spawning in if (CHudCrosshair *crosshair = GET_HUDELEMENT(CHudCrosshair)) @@ -1620,16 +1598,7 @@ void C_NEO_Player::Spawn( void ) m_nVisionLastTick = 0; m_bInLean = NEO_LEAN_NONE; - static_assert(_ARRAYSIZE(m_rfAttackersScores) == MAX_PLAYERS_ARRAY_SAFE); - static_assert(_ARRAYSIZE(m_rfAttackersAccumlator) == MAX_PLAYERS_ARRAY_SAFE); - static_assert(_ARRAYSIZE(m_rfAttackersHits) == MAX_PLAYERS_ARRAY_SAFE); - for (int i = 0; i < MAX_PLAYERS_ARRAY_SAFE; ++i) - { - m_rfAttackersScores.GetForModify(i) = 0; - m_rfAttackersAccumlator.GetForModify(i) = 0.0f; - m_rfAttackersHits.GetForModify(i) = 0; - } - if (IsLocalPlayer()) NeoUserIDsLocalKilledClear(); + ClearLocalPlayerDmgReports(); Weapon_SetZoom(false); @@ -2027,6 +1996,10 @@ void __MsgFunc_CSpectatorTakeoverPlayer(bf_read &msg) // Save for later in C_NEO_Player::OnDataChanged pSpectatorTakingOver->m_hSpectatorTakeoverPlayerTarget = pPlayerTakeoverTarget; pSpectatorTakingOver->m_bCopyOverTakeoverPlayerDetails = true; + if (pSpectatorTakingOver->IsLocalPlayer()) + { + NeoAllKDReportsClear(); + } } } @@ -2167,3 +2140,11 @@ void C_NEO_Player::PlayerUse() } } } + +void C_NEO_Player::ClearLocalPlayerDmgReports() +{ + if (IsLocalPlayer()) + { + NeoAllKDReportsClear(); + } +} diff --git a/src/game/client/neo/c_neo_player.h b/src/game/client/neo/c_neo_player.h index a2bef4f7c..607d7185e 100644 --- a/src/game/client/neo/c_neo_player.h +++ b/src/game/client/neo/c_neo_player.h @@ -172,9 +172,6 @@ class C_NEO_Player : public C_HL2MP_Player bool IsInVision() const { return m_bInVision; } bool IsInAim() const { return m_bInAim; } - int GetAttackersScores(const int attackerIdx) const; - int GetAttackerHits(const int attackerIdx) const; - const char *InternalGetNeoPlayerName() const; const char *GetNeoPlayerName() const; bool ClientWantNeoName() const; @@ -212,6 +209,8 @@ class C_NEO_Player : public C_HL2MP_Player bool IsAllowedToSuperJump(void); + void ClearLocalPlayerDmgReports(); + // Spectator takeover player related functionality bool IsAFK() const; bool IsFakePlayer() const; @@ -224,10 +223,6 @@ class C_NEO_Player : public C_HL2MP_Player CNetworkVar(int, m_iXP); CNetworkVar(int, m_iLoadoutWepChoice); CNetworkVar(int, m_iNextSpawnClassChoice); - - CNetworkArray(int, m_rfAttackersScores, MAX_PLAYERS_ARRAY_SAFE); - CNetworkArray(float, m_rfAttackersAccumlator, MAX_PLAYERS_ARRAY_SAFE); - CNetworkArray(int, m_rfAttackersHits, MAX_PLAYERS_ARRAY_SAFE); CNetworkVar(bool, m_bHasBeenAirborneForTooLongToSuperJump); diff --git a/src/game/client/neo/ui/neo_hud_deathnotice.cpp b/src/game/client/neo/ui/neo_hud_deathnotice.cpp index 42099f06a..463bcb68c 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_infos.h" +#include "c_neo_killer_damage_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_info.cpp b/src/game/client/neo/ui/neo_hud_killer_info.cpp index 86b558613..1da827597 100644 --- a/src/game/client/neo/ui/neo_hud_killer_info.cpp +++ b/src/game/client/neo/ui/neo_hud_killer_info.cpp @@ -3,7 +3,7 @@ #include "iclientmode.h" -#include "c_neo_killer_infos.h" +#include "c_neo_killer_damage_infos.h" #include "c_neo_player.h" #include "neo_gamerules.h" #include "vgui/ILocalize.h" @@ -17,7 +17,7 @@ // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" -DECLARE_NAMED_HUDELEMENT(CNEOHud_KillerInfo, neo_killer_damage_info) +DECLARE_NAMED_HUDELEMENT(CNEOHud_KillerInfo, neo_killer_info) NEO_HUD_ELEMENT_DECLARE_FREQ_CVAR(KillerInfo, 0.25) @@ -58,7 +58,7 @@ void CNEOHud_KillerInfo::CommonSetupHUD() void CNEOHud_KillerInfo::resetHUDState() { - V_memset(&g_neoKillerInfos, 0, sizeof(CNEOKillerInfos)); + NeoKillerInfoClear(); } void CNEOHud_KillerInfo::UpdateStateForNeoHudElementDraw() diff --git a/src/game/client/neo/ui/neo_scoreboard.cpp b/src/game/client/neo/ui/neo_scoreboard.cpp index 777741ef5..8b4fe7498 100644 --- a/src/game/client/neo/ui/neo_scoreboard.cpp +++ b/src/game/client/neo/ui/neo_scoreboard.cpp @@ -3,7 +3,7 @@ #include "c_neo_player.h" #include "neo_gamerules.h" #include "neo_theme.h" -#include "c_neo_killer_infos.h" +#include "c_neo_killer_damage_infos.h" #include #include @@ -331,7 +331,14 @@ void CNEOScoreBoard::Update() const bool bLocalPlaying = (TEAM_JINRAI == iLocalPlayerTeam || TEAM_NSF == iLocalPlayerTeam); const bool bIsTeamplay = NEORules()->IsTeamplay(); + const bool bShowDamageInfo = pLocalPlayer->IsPlayerDead() + && NEORules()->InRoundState() + && bLocalPlaying; + + // Zero array every update m_iTotalPlayers = 0; + V_memset(m_playersInfo, 0, sizeof(m_playersInfo)); + for (int i = 1; i <= gpGlobals->maxClients; ++i) { if (false == g_PR->IsConnected(i)) @@ -373,45 +380,46 @@ void CNEOScoreBoard::Update() pPlayerInfo->steamID.Clear(); } - const bool bIsPlaying = (TEAM_JINRAI == pPlayerInfo->iTeam || TEAM_NSF == pPlayerInfo->iTeam); - pPlayerInfo->iXP = bIsPlaying ? g_PR->GetXP(i) : 0; - pPlayerInfo->iDeaths = bIsPlaying ? g_PR->GetDeaths(i) : 0; + const bool bIsPlaying = ( + TEAM_JINRAI == pPlayerInfo->iTeam + || TEAM_NSF == pPlayerInfo->iTeam); - // 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)) + if (bIsPlaying) { - 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 = false; - for (int j = 0; j < g_neoUserIDsLocalKilledSize; ++j) + pPlayerInfo->iXP = g_PR->GetXP(i); + pPlayerInfo->iDeaths = g_PR->GetDeaths(i); + + // Damage infos + if (bShowDamageInfo) { - if (g_neoUserIDsLocalKilled[j] == pPlayerInfo->iUserID) + AttackersTotals *pDmgReport = nullptr; + for (int j = 0; j < g_neoDamageReportSize; ++j) { - pPlayerInfo->bYouKilled = true; - break; + if (g_neoDamageReport[j].iUserID == pPlayerInfo->iUserID) + { + pDmgReport = &g_neoDamageReport[j]; + break; + } + } + if (pDmgReport) + { + pPlayerInfo->iDealtDmgs = pDmgReport->iDealtDmgs; + pPlayerInfo->iDealtHits = pDmgReport->iDealtHits; + pPlayerInfo->iTakenDmgs = pDmgReport->iTakenDmgs; + pPlayerInfo->iTakenHits = pDmgReport->iTakenHits; + pPlayerInfo->bKilledYou = pNeoPlayer ? (pNeoPlayer->entindex() == g_neoKillerInfos.iEntIndex) : false; + pPlayerInfo->bYouKilled = false; + for (int j = 0; j < g_neoUserIDsLocalKilledSize; ++j) + { + if (g_neoUserIDsLocalKilled[j] == pPlayerInfo->iUserID) + { + pPlayerInfo->bYouKilled = true; + break; + } + } } } } - else - { - pPlayerInfo->iDealtDmgs = 0; - pPlayerInfo->iDealtHits = 0; - pPlayerInfo->iTakenDmgs = 0; - pPlayerInfo->iTakenHits = 0; - pPlayerInfo->bKilledYou = false; - pPlayerInfo->bYouKilled = false; - } // pPlayerInfo->iClass const bool bShowClass = @@ -459,7 +467,7 @@ void CNEOScoreBoard::Update() // pPlayerInfo->bDead pPlayerInfo->bDead = (false == g_PR->IsAlive(i)); - if (pPlayerInfo->iTeam == TEAM_JINRAI || pPlayerInfo->iTeam == TEAM_NSF) + if (bIsPlaying) { if (bIsImpersonating) { @@ -577,7 +585,6 @@ void CNEOScoreBoard::OnMainLoop(const NeoUI::Mode eMode) const bool bShowDamageInfo = pLocalPlayer->IsPlayerDead() && NEORules()->InRoundState() && g_neoKillerInfos.bHasDmgInfos - && bIsTeamplay && (pLocalPlayer->GetTeamNumber() == TEAM_JINRAI || pLocalPlayer->GetTeamNumber() == TEAM_NSF); static CrosshairInfo staticXhairInfo = {}; diff --git a/src/game/server/neo/neo_player.cpp b/src/game/server/neo/neo_player.cpp index 268ac07c4..7e04b45bc 100644 --- a/src/game/server/neo/neo_player.cpp +++ b/src/game/server/neo/neo_player.cpp @@ -75,10 +75,6 @@ SendPropTime(SENDINFO(m_flJumpLastTime)), SendPropTime(SENDINFO(m_flNextPingTime)), SendPropString(SENDINFO(m_pszTestMessage)), - -SendPropArray(SendPropInt(SENDINFO_ARRAY(m_rfAttackersScores)), m_rfAttackersScores), -SendPropArray(SendPropFloat(SENDINFO_ARRAY(m_rfAttackersAccumlator), -1, SPROP_COORD_MP_LOWPRECISION | SPROP_CHANGES_OFTEN, MIN_COORD_FLOAT, MAX_COORD_FLOAT), m_rfAttackersAccumlator), -SendPropArray(SendPropInt(SENDINFO_ARRAY(m_rfAttackersHits)), m_rfAttackersHits), SendPropArray(SendPropVector(SENDINFO_ARRAY(m_vLastPingByStar), -1, SPROP_COORD), m_vLastPingByStar), SendPropInt(SENDINFO(m_NeoFlags), 4, SPROP_UNSIGNED), @@ -119,10 +115,6 @@ DEFINE_FIELD(m_flNextPingTime, FIELD_TIME), DEFINE_FIELD(m_pszTestMessage, FIELD_STRING), -DEFINE_FIELD(m_rfAttackersScores, FIELD_CUSTOM), -DEFINE_FIELD(m_rfAttackersAccumlator, FIELD_CUSTOM), -DEFINE_FIELD(m_rfAttackersHits, FIELD_CUSTOM), - DEFINE_FIELD(m_NeoFlags, FIELD_CHARACTER), DEFINE_FIELD(m_szNeoName, FIELD_STRING), @@ -749,14 +741,32 @@ void CNEO_Player::Spawn(void) m_bAllowGibbing = true; m_bIneligibleForLoadoutPick = false; - static_assert(_ARRAYSIZE(m_rfAttackersScores) == MAX_PLAYERS_ARRAY_SAFE); - static_assert(_ARRAYSIZE(m_rfAttackersAccumlator) == MAX_PLAYERS_ARRAY_SAFE); - static_assert(_ARRAYSIZE(m_rfAttackersHits) == MAX_PLAYERS_ARRAY_SAFE); - for (int i = 0; i < MAX_PLAYERS_ARRAY_SAFE; ++i) + static_assert(_ARRAYSIZE(m_riAttackersScores) == MAX_PLAYERS_ARRAY_SAFE); + static_assert(_ARRAYSIZE(m_rflAttackersAccumlator) == MAX_PLAYERS_ARRAY_SAFE); + static_assert(_ARRAYSIZE(m_riAttackersHits) == MAX_PLAYERS_ARRAY_SAFE); + V_memset(m_riAttackersScores, 0, sizeof(m_riAttackersScores)); + V_memset(m_rflAttackersAccumlator, 0, sizeof(m_rflAttackersAccumlator)); + V_memset(m_riAttackersHits, 0, sizeof(m_riAttackersHits)); + + // Also set zero on other player's held stats of this player's index, needed + // for gamemodes where player respawn within a round + const int thisIdx = entindex(); + for (int pIdx = 1; pIdx <= gpGlobals->maxClients; ++pIdx) { - m_rfAttackersScores.GetForModify(i) = 0; - m_rfAttackersAccumlator.GetForModify(i) = 0.0f; - m_rfAttackersHits.GetForModify(i) = 0; + if (pIdx == thisIdx) + { + continue; + } + + auto *pNeoOther = static_cast(UTIL_PlayerByIndex(pIdx)); + if (!pNeoOther || pNeoOther->IsHLTV()) + { + continue; + } + + pNeoOther->m_riAttackersScores[thisIdx] = 0; + pNeoOther->m_rflAttackersAccumlator[thisIdx] = 0.0f; + pNeoOther->m_riAttackersHits[thisIdx] = 0; } m_flRanOutSprintTime = 0.0f; @@ -2347,6 +2357,106 @@ void CNEO_Player::StartShowDmgStats(const CTakeDamageInfo *info) } WRITE_SHORT(attackerIdx); WRITE_STRING(killedWithName); + + AttackersTotals atkTotals[MAX_PLAYERS_ARRAY_SAFE] = {}; + int iAtkSize = 0; + int iMaxDmgs = 0; + int iMaxHits = 0; + + // Send server's per-player damage stats. This is the proper damage and + // hit count on player's death. + const int thisIdx = entindex(); + for (int pIdx = 1; pIdx <= gpGlobals->maxClients; ++pIdx) + { + if (pIdx == thisIdx) + { + continue; + } + + auto *pNeoOther = static_cast(UTIL_PlayerByIndex(pIdx)); + if (!pNeoOther || pNeoOther->IsHLTV()) + { + continue; + } + + const int iDealtDmgs = pNeoOther->m_riAttackersScores[thisIdx]; + const int iDealtHits = pNeoOther->m_riAttackersHits[thisIdx]; + const int iTakenDmgs = m_riAttackersScores[pIdx]; + const int iTakenHits = m_riAttackersHits[pIdx]; + + if ((iDealtDmgs > 0 && iDealtHits > 0) || (iTakenDmgs > 0 && iTakenHits > 0)) + { + AttackersTotals *atk = &atkTotals[iAtkSize++]; + atk->iUserID = pNeoOther->GetUserID(); + atk->iDealtDmgs = iDealtDmgs; + atk->iDealtHits = iDealtHits; + atk->iTakenDmgs = iTakenDmgs; + atk->iTakenHits = iTakenHits; + + iMaxDmgs = Max(iMaxDmgs, Max(iTakenDmgs, iDealtDmgs)); + iMaxHits = Max(iMaxHits, Max(iTakenHits, iDealtHits)); + } + } + + // CTG will never hit more than 255 per person, and improbable for hits per person + // But for juggernaut or respawns in a round, this can happen + ENEOCompactMsgFlag flags = 0; + if (iMaxDmgs <= UCHAR_MAX) flags |= NEO_COMPACT_MSG_FLAG_DMGS; + if (iMaxHits <= UCHAR_MAX) flags |= NEO_COMPACT_MSG_FLAG_HITS; + + const int iWriteSize = 2 + (V_strlen(killedWithName) + 1) + 1 + 1; + + int iDmgInfoWriteSize = 4; + iDmgInfoWriteSize += (flags & NEO_COMPACT_MSG_FLAG_DMGS) ? 2 : 4; + iDmgInfoWriteSize += (flags & NEO_COMPACT_MSG_FLAG_HITS) ? 2 : 4; + + // Improbable it'll happen but just in-case + int iAtkFirstSize = iAtkSize; + if ((iWriteSize + (iAtkSize * iDmgInfoWriteSize)) > MAX_USER_MSG_DATA) + { + const int iFreeSpace = MAX_USER_MSG_DATA - iWriteSize; + iAtkFirstSize = iFreeSpace / iDmgInfoWriteSize; + flags |= NEO_COMPACT_MSG_FLAG_EXTRA; + } + + // MAX_PLAYERS fits in a byte + WRITE_BYTE(static_cast(iAtkFirstSize)); + WRITE_BYTE(flags); + + for (int i = 0; i < iAtkSize; ++i) + { + if (i == iAtkFirstSize) + { + MessageEnd(); + UserMessageBegin(filter, "KillerDamageInfoExtra"); + WRITE_BYTE(static_cast(iAtkSize - iAtkFirstSize)); + WRITE_BYTE(flags); + } + + const AttackersTotals *atk = &atkTotals[i]; + WRITE_LONG(atk->iUserID); + if (flags & NEO_COMPACT_MSG_FLAG_DMGS) + { + WRITE_BYTE(static_cast(atk->iDealtDmgs)); + WRITE_BYTE(static_cast(atk->iTakenDmgs)); + } + else + { + WRITE_SHORT(static_cast(atk->iDealtDmgs)); + WRITE_SHORT(static_cast(atk->iTakenDmgs)); + } + + if (flags & NEO_COMPACT_MSG_FLAG_HITS) + { + WRITE_BYTE(static_cast(atk->iDealtHits)); + WRITE_BYTE(static_cast(atk->iTakenHits)); + } + else + { + WRITE_SHORT(static_cast(atk->iDealtHits)); + WRITE_SHORT(static_cast(atk->iTakenHits)); + } + } } MessageEnd(); } @@ -3306,46 +3416,6 @@ bool CNEO_Player::ProcessTeamSwitchRequest(int iTeam) return true; } -int CNEO_Player::GetAttackersScores(const int attackerIdx) const -{ - if (NEORules()->GetGameType() == NEO_GAME_TYPE_DM || NEORules()->GetGameType() == NEO_GAME_TYPE_TDM) - { - return m_rfAttackersScores.Get(attackerIdx); - } - return m_rfAttackersScores.Get(attackerIdx); -} - -int CNEO_Player::GetAttackerHits(const int attackerIdx) const -{ - return m_rfAttackersHits.Get(attackerIdx); -} - -AttackersTotals CNEO_Player::GetAttackersTotals() const -{ - AttackersTotals totals = {}; - - const int thisIdx = entindex(); - for (int pIdx = 1; pIdx <= gpGlobals->maxClients; ++pIdx) - { - if (pIdx == thisIdx) - { - continue; - } - - auto* neoAttacker = static_cast(UTIL_PlayerByIndex(pIdx)); - if (!neoAttacker || neoAttacker->IsHLTV()) - { - continue; - } - - totals.dealtDmgs += neoAttacker->GetAttackersScores(thisIdx); - totals.takenDmgs += GetAttackersScores(pIdx); - totals.dealtHits += neoAttacker->GetAttackerHits(thisIdx); - totals.takenHits += GetAttackerHits(pIdx); - } - return totals; -} - int CNEO_Player::OnTakeDamage_Alive(const CTakeDamageInfo& info) { NEORules()->SetLastHurt(entindex()); @@ -3369,6 +3439,7 @@ int CNEO_Player::OnTakeDamage_Alive(const CTakeDamageInfo& info) if (auto *attacker = ToNEOPlayer(info.GetAttacker())) { CNEO_Player* pImpersonated = attacker->GetSpectatorTakeoverPlayerTarget(); + const int attackerRecIdx = attacker->entindex(); // Record goes to the impersonator's original index const int attackerIdx = pImpersonated ? pImpersonated->entindex() : attacker->entindex(); NEORules()->SetLastAttacker(entindex()); // NEO TODO (Adam) Once we can spectate non-players, let last attacker be non-neoplayer (Jeff) @@ -3376,7 +3447,7 @@ int CNEO_Player::OnTakeDamage_Alive(const CTakeDamageInfo& info) const float flFractionalDamage = info.GetDamage() - floor(info.GetDamage()); int iDamage = static_cast(info.GetDamage() - flFractionalDamage); - float flDmgAccumlator = m_rfAttackersAccumlator.Get(attackerIdx); + float flDmgAccumlator = m_rflAttackersAccumlator[attackerRecIdx]; flDmgAccumlator += flFractionalDamage; if (flDmgAccumlator >= 1.0f) { @@ -3414,9 +3485,9 @@ int CNEO_Player::OnTakeDamage_Alive(const CTakeDamageInfo& info) // Apply damages/hits numbers if (iDamage > 0) { - m_rfAttackersScores.GetForModify(attackerIdx) += Min(iDamage, GetHealth()); - m_rfAttackersAccumlator.Set(attackerIdx, flDmgAccumlator); - m_rfAttackersHits.GetForModify(attackerIdx) += info.GetNumDamageEvents(); + m_riAttackersScores[attackerRecIdx] += Min(iDamage, GetHealth()); + m_rflAttackersAccumlator[attackerRecIdx] = flDmgAccumlator; + m_riAttackersHits[attackerRecIdx] += info.GetNumDamageEvents(); if (bIsTeamDmg && sv_neo_teamdamage_kick.GetBool() && NEORules()->IsRoundLive()) { @@ -4289,6 +4360,29 @@ void CNEO_Player::SpectatorTakeoverPlayerPreThink() m_bInVision = pPlayerTakeoverTarget->m_bInVision; m_nVisionLastTick = pPlayerTakeoverTarget->m_nVisionLastTick; + // Just clear this so the attackers scores/hits are based on only when it's + // impersonated not including the bot controlled part + const int thisIdx = entindex(); + V_memset(m_riAttackersScores, 0, sizeof(m_riAttackersScores)); + V_memset(m_rflAttackersAccumlator, 0, sizeof(m_rflAttackersAccumlator)); + V_memset(m_riAttackersHits, 0, sizeof(m_riAttackersHits)); + for (int pIdx = 1; pIdx <= gpGlobals->maxClients; ++pIdx) + { + if (pIdx == thisIdx) + { + continue; + } + + auto *pNeoOther = static_cast(UTIL_PlayerByIndex(pIdx)); + if (!pNeoOther || pNeoOther->IsHLTV()) + { + continue; + } + + pNeoOther->m_riAttackersScores[thisIdx] = 0; + pNeoOther->m_rflAttackersAccumlator[thisIdx] = 0.0f; + pNeoOther->m_riAttackersHits[thisIdx] = 0; + } // Transfer weapons from the takeover target. RemoveAllItems(false); diff --git a/src/game/server/neo/neo_player.h b/src/game/server/neo/neo_player.h index 60b67614c..7e5f7ac8a 100644 --- a/src/game/server/neo/neo_player.h +++ b/src/game/server/neo/neo_player.h @@ -226,13 +226,9 @@ class CNEO_Player : public CHL2MP_Player int ShouldTransmit( const CCheckTransmitInfo *pInfo) OVERRIDE; - int GetAttackersScores(const int attackerIdx) const; - int GetAttackerHits(const int attackerIdx) const; - void SetNameDupePos(const int dupePos); int NameDupePos() const; - AttackersTotals GetAttackersTotals() const; void StartShowDmgStats(const CTakeDamageInfo *info); void AddPoints(int score, bool bAllowNegativeScore, bool bIgnorePlayerTakeover = false); @@ -308,9 +304,9 @@ class CNEO_Player : public CHL2MP_Player CNetworkVar(float, m_flNextPingTime); // Used as 1-indexed, need MAX_PLAYERS_ARRAY_SAFE - CNetworkArray(int, m_rfAttackersScores, MAX_PLAYERS_ARRAY_SAFE); - CNetworkArray(float, m_rfAttackersAccumlator, MAX_PLAYERS_ARRAY_SAFE); - CNetworkArray(int, m_rfAttackersHits, MAX_PLAYERS_ARRAY_SAFE); + int m_riAttackersScores[MAX_PLAYERS_ARRAY_SAFE]; + float m_rflAttackersAccumlator[MAX_PLAYERS_ARRAY_SAFE]; + int m_riAttackersHits[MAX_PLAYERS_ARRAY_SAFE]; CNetworkVar(unsigned char, m_NeoFlags); CNetworkString(m_szNeoName, MAX_PLAYER_NAME_LENGTH); diff --git a/src/game/shared/hl2/hl2_usermessages.cpp b/src/game/shared/hl2/hl2_usermessages.cpp index a02951b3f..1c06c8976 100644 --- a/src/game/shared/hl2/hl2_usermessages.cpp +++ b/src/game/shared/hl2/hl2_usermessages.cpp @@ -49,6 +49,7 @@ void RegisterUserMessages( void ) #ifdef NEO usermessages->Register( "KillerDamageInfo", -1 ); + usermessages->Register( "KillerDamageInfoExtra", -1 ); usermessages->Register( "IdleRespawnShowMenu", -1 ); usermessages->Register( "CSpectatorTakeoverPlayer", -1 ); usermessages->Register( "AchievementMark", -1 ); diff --git a/src/game/shared/neo/neo_gamerules.cpp b/src/game/shared/neo/neo_gamerules.cpp index 30f905336..b6faeae68 100644 --- a/src/game/shared/neo/neo_gamerules.cpp +++ b/src/game/shared/neo/neo_gamerules.cpp @@ -3968,7 +3968,6 @@ void CNEORules::SetWinningTeam(int team, int iWinReason, bool bForceMapReset, bo m_bGotMatchWinner = gotMatchWinner; m_iMatchWinner = team; } -#endif // NEO JANK (nullsystem): Dont like how this is fetched twice (PlayerKilled, DeathNotice), // but blame the structure of the base classes @@ -3994,7 +3993,7 @@ static CNEO_Player* FetchAssists(CNEO_Player* attacker, CNEO_Player* victim) continue; } - const int assistDmg = victim->GetAttackersScores(assistIdx); + const int assistDmg = victim->m_riAttackersScores[assistIdx]; static const float MIN_DMG_QUALIFY_ASSIST = 0.5f; if ((float)assistDmg / victim->GetMaxHealth() >= MIN_DMG_QUALIFY_ASSIST) { @@ -4004,7 +4003,6 @@ static CNEO_Player* FetchAssists(CNEO_Player* attacker, CNEO_Player* victim) return NULL; } -#ifdef GAME_DLL void CNEORules::CheckIfCapPrevent(CNEO_Player *capPreventerPlayer) { if (!NEO_GAME_TYPE_SETTINGS[GetGameType()].capPrevent) @@ -4069,6 +4067,7 @@ void CNEORules::PlayerKilled(CBasePlayer *pVictim, const CTakeDamageInfo &info) { BaseClass::PlayerKilled(pVictim, info); +#ifdef GAME_DLL auto attacker = dynamic_cast(info.GetAttacker()); auto victim = dynamic_cast(pVictim); auto grenade = dynamic_cast(info.GetInflictor()); @@ -4080,10 +4079,8 @@ void CNEORules::PlayerKilled(CBasePlayer *pVictim, const CTakeDamageInfo &info) if (m_nRoundStatus == NeoRoundStatus::Pause) { -#ifdef GAME_DLL // Counter-act the death count for pausing state victim->IncrementDeathCount(-1); -#endif return; } @@ -4091,24 +4088,19 @@ void CNEORules::PlayerKilled(CBasePlayer *pVictim, const CTakeDamageInfo &info) if (attacker == victim || (!attacker && !grenade)) { victim->AddPoints(-1, true); -#ifdef GAME_DLL CheckIfCapPrevent(victim); -#endif } -#ifdef GAME_DLL else if (!attacker && grenade && grenade->GetTeamNumber() == victim->GetTeamNumber()) { // Death by own team's grenade, but the player is already disconnected. Check for cap prevent. CheckIfCapPrevent(victim); } -#endif else if (attacker) { // Team kill if (IsTeamplay() && attacker->GetTeamNumber() == victim->GetTeamNumber()) { attacker->AddPoints(-1, true); -#ifdef GAME_DLL if (sv_neo_teamdamage_kick.GetBool() && IsRoundLive()) { ++attacker->m_iTeamKillsInflicted; @@ -4127,13 +4119,11 @@ void CNEORules::PlayerKilled(CBasePlayer *pVictim, const CTakeDamageInfo &info) break; } } -#endif } // Enemy kill else { attacker->AddPoints(1, false); -#ifdef GAME_DLL if (GetGameType() == NEO_GAME_TYPE_JGR && IsRoundLive()) { if (attacker->GetClass() == NEO_CLASS_JUGGERNAUT) @@ -4152,7 +4142,6 @@ void CNEORules::PlayerKilled(CBasePlayer *pVictim, const CTakeDamageInfo &info) } } } -#endif } } @@ -4177,6 +4166,7 @@ void CNEORules::PlayerKilled(CBasePlayer *pVictim, const CTakeDamageInfo &info) assister->AddPoints(1, false, true); } } +#endif // GAME_DLL } #ifdef GAME_DLL diff --git a/src/game/shared/neo/neo_player_shared.h b/src/game/shared/neo/neo_player_shared.h index a37a7b5d6..a0368e340 100644 --- a/src/game/shared/neo/neo_player_shared.h +++ b/src/game/shared/neo/neo_player_shared.h @@ -316,20 +316,24 @@ void UpdatePingCommands(CNEO_Player* player, const Vector& pingPos); struct AttackersTotals { - int dealtDmgs; - int dealtHits; - int takenDmgs; - int takenHits; - - void operator+=(const AttackersTotals &other) - { - dealtDmgs += other.dealtDmgs; - dealtHits += other.dealtHits; - takenDmgs += other.takenDmgs; - takenHits += other.takenHits; - } + int iUserID; + int iDealtDmgs; + int iDealtHits; + int iTakenDmgs; + int iTakenHits; }; +enum ENEOCompactMsgFlag_ : unsigned char +{ + NEO_COMPACT_MSG_FLAG_NIL = 0, + NEO_COMPACT_MSG_FLAG_DMGS = (1 << 0), + NEO_COMPACT_MSG_FLAG_HITS = (1 << 1), + NEO_COMPACT_MSG_FLAG_EXTRA = (1 << 2), +}; + +typedef unsigned char ENEOCompactMsgFlag; + + [[deprecated]] void KillerLineStr(char* killByLine, const int killByLineMax, CNEO_Player* neoAttacker, const CNEO_Player* neoVictim, const char* killedWith = "");