From ac8e0921ad4f144e512035d188dfb2789def86f8 Mon Sep 17 00:00:00 2001 From: jhh8 Date: Tue, 25 Mar 2025 00:09:06 +0200 Subject: [PATCH 1/2] cl_showspectators --- src/game/client/swarm/c_asw_player.cpp | 1 + src/game/client/swarm/c_asw_player.h | 1 + src/game/client/vgui_fpspanel.cpp | 64 ++++++++++++++++++++++++-- src/game/server/swarm/asw_player.cpp | 23 ++++++++- src/game/server/swarm/asw_player.h | 1 + 5 files changed, 84 insertions(+), 6 deletions(-) diff --git a/src/game/client/swarm/c_asw_player.cpp b/src/game/client/swarm/c_asw_player.cpp index 5bb9a4abf..a975518a3 100644 --- a/src/game/client/swarm/c_asw_player.cpp +++ b/src/game/client/swarm/c_asw_player.cpp @@ -248,6 +248,7 @@ BEGIN_NETWORK_TABLE( C_ASW_Player, DT_ASW_Player ) RecvPropQAngles( RECVINFO( m_angEyeAngles ) ), RecvPropEHandle( RECVINFO( m_hInhabiting ) ), RecvPropEHandle( RECVINFO( m_hSpectating ) ), + RecvPropInt( RECVINFO( m_iSpectatorIndexes ) ), RecvPropInt( RECVINFO( m_iHealth ) ), RecvPropEHandle( RECVINFO( m_pCurrentInfoMessage ) ), RecvPropFloat( RECVINFO( m_fMarineDeathTime ) ), diff --git a/src/game/client/swarm/c_asw_player.h b/src/game/client/swarm/c_asw_player.h index a029118f5..ce5ba0942 100644 --- a/src/game/client/swarm/c_asw_player.h +++ b/src/game/client/swarm/c_asw_player.h @@ -146,6 +146,7 @@ class C_ASW_Player : public C_BasePlayer, public IASWPlayerAnimStateHelpers CInterpolatedVar< QAngle > m_iv_angEyeAngles; CNetworkHandle( C_ASW_Inhabitable_NPC, m_hInhabiting ); // our currently controlled marine CNetworkHandle( C_ASW_Inhabitable_NPC, m_hSpectating ); // the marine we're spectating when dead + CNetworkVar( int, m_iSpectatorIndexes ); const Vector& GetCrosshairTracePos() { return m_vecCrosshairTracePos; } void SetCrosshairTracePos( const Vector &vecPos ) { m_vecCrosshairTracePos = vecPos; } Vector m_vecCrosshairTracePos; // the world location directly beneath the player's crosshair diff --git a/src/game/client/vgui_fpspanel.cpp b/src/game/client/vgui_fpspanel.cpp index ade19d568..0e358ef94 100644 --- a/src/game/client/vgui_fpspanel.cpp +++ b/src/game/client/vgui_fpspanel.cpp @@ -19,6 +19,7 @@ #include "../common/xbox/xboxstubs.h" #ifdef INFESTED_DLL #include "c_asw_player.h" +#include "c_playerresource.h" #include "c_asw_inhabitable_npc.h" #endif @@ -27,6 +28,7 @@ static ConVar cl_showfps( "cl_showfps", "0", FCVAR_RELEASE, "Draw fps meter at top of screen (1 = fps, 2 = smooth fps, 3 = server MS, 4 = Show FPS and Log to file )" ); static ConVar cl_showpos( "cl_showpos", "0", FCVAR_RELEASE, "Draw current position at top of screen (1 = eyes, 2 = feet)" ); +static ConVar cl_showspectators( "cl_showspectators", "0", FCVAR_RELEASE, "Draw a list of spectators" ); #ifdef INFESTED_DLL static ConVar cl_showpos_npc( "cl_showpos_npc", "-1", FCVAR_NONE, "If the player is controlling or spectating an NPC, use the NPC's position, angles, and velocity instead; -1 = 1 if asw_allow_detach is 0, 0 otherwise" ); extern ConVar asw_allow_detach; @@ -184,7 +186,8 @@ bool CFPSPanel::ShouldDraw( void ) if ( g_bDisplayParticlePerformance ) return true; if ( ( !cl_showfps.GetInt() || ( gpGlobals->absoluteframetime <= 0 ) ) && - ( !cl_showpos.GetInt() ) ) + ( !cl_showpos.GetInt() ) && + ( !cl_showspectators.GetInt() ) ) { m_bLastDraw = false; return false; @@ -278,12 +281,12 @@ void CFPSPanel::Paint() float flTotalTime = 0.0f; float flPeakTime = 0.0f; - for ( int i = 0; i < SERVER_TIME_HISTORY; ++i ) + for ( int j = 0; j < SERVER_TIME_HISTORY; ++j ) { - flTotalTime += m_pServerTimes[i]; - if ( flPeakTime < m_pServerTimes[i] ) + flTotalTime += m_pServerTimes[j]; + if ( flPeakTime < m_pServerTimes[j] ) { - flPeakTime = m_pServerTimes[i]; + flPeakTime = m_pServerTimes[j]; } } flTotalTime /= SERVER_TIME_HISTORY; @@ -517,6 +520,57 @@ void CFPSPanel::Paint() } } + if ( cl_showspectators.GetInt() ) + { + FOR_EACH_VALID_SPLITSCREEN_PLAYER( hh ) + { + C_ASW_Player *pPlayer = C_ASW_Player::GetLocalASWPlayer( hh ); + if ( !pPlayer ) + continue; + + C_ASW_Inhabitable_NPC *pSpectating = pPlayer->m_hSpectating; + if ( pSpectating && pSpectating->GetCommander() ) + { + pPlayer = pSpectating->GetCommander(); + } + + i += 3; + + int nSpecCount = 0; + for ( int id = 1; id < 31; id += 1 ) + { + if ( !g_PR->IsConnected( id ) ) + continue; + + if ( g_PR->IsFakePlayer( id ) ) + continue; + + if ( !( pPlayer->m_iSpectatorIndexes.Get() & static_cast( pow( 2, id ) ) ) ) + continue; + + nSpecCount++; + + i++; + + char szSpecName[k_cchPersonaNameMax] = { 0 }; + Q_strncpy( szSpecName, g_PR->GetPlayerName( id ), sizeof( szSpecName ) ); + g_pMatSystemSurface->DrawColoredText( m_hFont, x, 2 + i * lineHeight, + 255, 255, 255, 255, + " %s", szSpecName ); + } + + if ( nSpecCount == 0 ) + break; + + char szName[k_cchPersonaNameMax] = { 0 }; + Q_strncpy( szName, pPlayer->GetPlayerName(), sizeof( szName ) ); + + g_pMatSystemSurface->DrawColoredText( m_hFont, x, 2 + ( i - nSpecCount ) * lineHeight, + 255, 255, 255, 255, + "Spectating %s (%d):", szName, nSpecCount ); + } + } + if ( m_nLinesNeeded != i ) { m_nLinesNeeded = i; diff --git a/src/game/server/swarm/asw_player.cpp b/src/game/server/swarm/asw_player.cpp index 2a5e7e72f..5716399b0 100644 --- a/src/game/server/swarm/asw_player.cpp +++ b/src/game/server/swarm/asw_player.cpp @@ -221,6 +221,7 @@ IMPLEMENT_SERVERCLASS_ST( CASW_Player, DT_ASW_Player ) SendPropQAngles( SENDINFO( m_angEyeAngles ), 10, SPROP_CHANGES_OFTEN, SendProxy_QAngles, SENDPROP_PLAYER_EYE_ANGLES_PRIORITY ), SendPropEHandle( SENDINFO( m_hInhabiting ) ), SendPropEHandle( SENDINFO( m_hSpectating ) ), + SendPropInt( SENDINFO( m_iSpectatorIndexes ) ), SendPropFloat( SENDINFO( m_fMarineDeathTime ) ), SendPropEHandle( SENDINFO( m_hOrderingMarine ) ), SendPropEHandle( SENDINFO( m_pCurrentInfoMessage ) ), @@ -250,6 +251,7 @@ BEGIN_DATADESC( CASW_Player ) DEFINE_FIELD( m_vecLastMarineOrigin, FIELD_VECTOR ), DEFINE_FIELD( m_hInhabiting, FIELD_EHANDLE ), DEFINE_FIELD( m_hSpectating, FIELD_EHANDLE ), + DEFINE_FIELD( m_iSpectatorIndexes, FIELD_INTEGER ), DEFINE_FIELD( m_vecStoredPosition, FIELD_VECTOR ), DEFINE_FIELD( m_pCurrentInfoMessage, FIELD_EHANDLE ), DEFINE_FIELD( m_iUseEntities, FIELD_INTEGER ), @@ -441,6 +443,8 @@ CASW_Player::~CASW_Player() m_PlayerAnimState->Release(); if ( ASWGameRules() ) ASWGameRules()->SetMaxMarines( this ); + + SetSpectatingNPC( NULL ); } //------------------------------------------------------------------------------ @@ -556,9 +560,9 @@ void CASW_Player::PostThink() if (found_available_marine) { DevMsg(" Riflemod Drop-In. Switching player to marine 0\n"); + SetSpectatingNPC( NULL ); pBotMarine->SetCommander( this ); pBotMarine->GetMarineResource()->SetCommander( this ); - SetSpectatingNPC( NULL ); SwitchMarine( 0, false ); // reactivedrop: when player took marine under control // delay his primary attack to prevent immediate shooting at @@ -693,6 +697,7 @@ void CASW_Player::Spawn() m_nChangingSlot = 0; m_bHasAwardedXP = false; m_bSentPromotedMessage = false; + m_iSpectatorIndexes = 0; m_flLastActiveTime = gpGlobals->curtime; @@ -2213,7 +2218,23 @@ void CASW_Player::SpectateNextMarine() void CASW_Player::SetSpectatingNPC( CASW_Inhabitable_NPC *pSpectating ) { + int nOwnIndex = pow( 2, GetClientIndex() + 1 ); + CASW_Inhabitable_NPC *pSpectatingPrevious = GetSpectatingNPC(); + if ( pSpectatingPrevious && pSpectatingPrevious->GetCommander() ) + { + pSpectatingPrevious->GetCommander()->m_iSpectatorIndexes -= nOwnIndex; + } + m_hSpectating = pSpectating; + + if ( !pSpectating ) + return; + + CASW_Player *pPlayer = pSpectating->GetCommander(); + if ( !pPlayer ) + return; + + pPlayer->m_iSpectatorIndexes += nOwnIndex; } CASW_Inhabitable_NPC *CASW_Player::GetSpectatingNPC() const diff --git a/src/game/server/swarm/asw_player.h b/src/game/server/swarm/asw_player.h index da6b20ad6..cba90bfdd 100644 --- a/src/game/server/swarm/asw_player.h +++ b/src/game/server/swarm/asw_player.h @@ -91,6 +91,7 @@ class CASW_Player : public CBaseMultiplayerPlayer, public IASWPlayerAnimStateHel CASW_Inhabitable_NPC *GetSpectatingNPC() const; HSCRIPT ScriptGetSpectatingNPC() const; CNetworkHandle( CASW_Inhabitable_NPC, m_hSpectating ); + CNetworkVar( int, m_iSpectatorIndexes ); bool m_bLastAttackButton; // used to detect left clicks for cycling through marines bool m_bLastAttack2Button; // used to detect right clicks for cycling through marines bool m_bRequestedSpectator; // this player requested to be a spectator since the start of a match (won't be considered for leader, campaign votes, etc.) From 98ecbad7edbf22177ee3ac0e02d13fedf7d000df Mon Sep 17 00:00:00 2001 From: jhh8 Date: Wed, 26 Mar 2025 20:51:01 +0200 Subject: [PATCH 2/2] bitwise operator saga begins --- src/game/client/swarm/c_asw_player.h | 2 +- src/game/client/vgui_fpspanel.cpp | 8 +++++--- src/game/server/swarm/asw_player.cpp | 6 +++--- src/game/server/swarm/asw_player.h | 2 +- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/game/client/swarm/c_asw_player.h b/src/game/client/swarm/c_asw_player.h index ce5ba0942..6cd47a4de 100644 --- a/src/game/client/swarm/c_asw_player.h +++ b/src/game/client/swarm/c_asw_player.h @@ -146,7 +146,7 @@ class C_ASW_Player : public C_BasePlayer, public IASWPlayerAnimStateHelpers CInterpolatedVar< QAngle > m_iv_angEyeAngles; CNetworkHandle( C_ASW_Inhabitable_NPC, m_hInhabiting ); // our currently controlled marine CNetworkHandle( C_ASW_Inhabitable_NPC, m_hSpectating ); // the marine we're spectating when dead - CNetworkVar( int, m_iSpectatorIndexes ); + CNetworkVar( unsigned int, m_iSpectatorIndexes ); const Vector& GetCrosshairTracePos() { return m_vecCrosshairTracePos; } void SetCrosshairTracePos( const Vector &vecPos ) { m_vecCrosshairTracePos = vecPos; } Vector m_vecCrosshairTracePos; // the world location directly beneath the player's crosshair diff --git a/src/game/client/vgui_fpspanel.cpp b/src/game/client/vgui_fpspanel.cpp index 0e358ef94..1a5dc4f0d 100644 --- a/src/game/client/vgui_fpspanel.cpp +++ b/src/game/client/vgui_fpspanel.cpp @@ -537,7 +537,7 @@ void CFPSPanel::Paint() i += 3; int nSpecCount = 0; - for ( int id = 1; id < 31; id += 1 ) + for ( int id = 1; id <= 32; id += 1 ) { if ( !g_PR->IsConnected( id ) ) continue; @@ -545,11 +545,10 @@ void CFPSPanel::Paint() if ( g_PR->IsFakePlayer( id ) ) continue; - if ( !( pPlayer->m_iSpectatorIndexes.Get() & static_cast( pow( 2, id ) ) ) ) + if ( !( pPlayer->m_iSpectatorIndexes.Get() & ( 1u << id ) ) ) continue; nSpecCount++; - i++; char szSpecName[k_cchPersonaNameMax] = { 0 }; @@ -560,7 +559,10 @@ void CFPSPanel::Paint() } if ( nSpecCount == 0 ) + { + i -= 3; break; + } char szName[k_cchPersonaNameMax] = { 0 }; Q_strncpy( szName, pPlayer->GetPlayerName(), sizeof( szName ) ); diff --git a/src/game/server/swarm/asw_player.cpp b/src/game/server/swarm/asw_player.cpp index 5716399b0..73d026bc9 100644 --- a/src/game/server/swarm/asw_player.cpp +++ b/src/game/server/swarm/asw_player.cpp @@ -2218,11 +2218,11 @@ void CASW_Player::SpectateNextMarine() void CASW_Player::SetSpectatingNPC( CASW_Inhabitable_NPC *pSpectating ) { - int nOwnIndex = pow( 2, GetClientIndex() + 1 ); + unsigned int nOwnIndex = 1u << ( GetClientIndex() + 1 ); CASW_Inhabitable_NPC *pSpectatingPrevious = GetSpectatingNPC(); if ( pSpectatingPrevious && pSpectatingPrevious->GetCommander() ) { - pSpectatingPrevious->GetCommander()->m_iSpectatorIndexes -= nOwnIndex; + pSpectatingPrevious->GetCommander()->m_iSpectatorIndexes &=~ nOwnIndex; } m_hSpectating = pSpectating; @@ -2234,7 +2234,7 @@ void CASW_Player::SetSpectatingNPC( CASW_Inhabitable_NPC *pSpectating ) if ( !pPlayer ) return; - pPlayer->m_iSpectatorIndexes += nOwnIndex; + pPlayer->m_iSpectatorIndexes |= nOwnIndex; } CASW_Inhabitable_NPC *CASW_Player::GetSpectatingNPC() const diff --git a/src/game/server/swarm/asw_player.h b/src/game/server/swarm/asw_player.h index cba90bfdd..b920161ec 100644 --- a/src/game/server/swarm/asw_player.h +++ b/src/game/server/swarm/asw_player.h @@ -91,7 +91,7 @@ class CASW_Player : public CBaseMultiplayerPlayer, public IASWPlayerAnimStateHel CASW_Inhabitable_NPC *GetSpectatingNPC() const; HSCRIPT ScriptGetSpectatingNPC() const; CNetworkHandle( CASW_Inhabitable_NPC, m_hSpectating ); - CNetworkVar( int, m_iSpectatorIndexes ); + CNetworkVar( unsigned int, m_iSpectatorIndexes ); bool m_bLastAttackButton; // used to detect left clicks for cycling through marines bool m_bLastAttack2Button; // used to detect right clicks for cycling through marines bool m_bRequestedSpectator; // this player requested to be a spectator since the start of a match (won't be considered for leader, campaign votes, etc.)