summaryrefslogtreecommitdiff
path: root/game/server/tf/tf_player_resource.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'game/server/tf/tf_player_resource.cpp')
-rw-r--r--game/server/tf/tf_player_resource.cpp417
1 files changed, 417 insertions, 0 deletions
diff --git a/game/server/tf/tf_player_resource.cpp b/game/server/tf/tf_player_resource.cpp
new file mode 100644
index 0000000..37ec33a
--- /dev/null
+++ b/game/server/tf/tf_player_resource.cpp
@@ -0,0 +1,417 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: TF's custom CPlayerResource
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "tf_player.h"
+#include "player_resource.h"
+#include "tf_player_resource.h"
+#include "tf_gamestats.h"
+#include "tf_gamerules.h"
+#include <coordsize.h>
+#include "tf_matchmaking_shared.h"
+
+#include "tf_mann_vs_machine_stats.h"
+#include "player_vs_environment/tf_population_manager.h"
+#include "tf_gc_server.h"
+
+#define STATS_SEND_FREQUENCY 1.f
+
+// Datatable
+IMPLEMENT_SERVERCLASS_ST( CTFPlayerResource, DT_TFPlayerResource )
+ SendPropArray3( SENDINFO_ARRAY3( m_iTotalScore ), SendPropInt( SENDINFO_ARRAY( m_iTotalScore ), -1, SPROP_UNSIGNED | SPROP_VARINT ) ),
+ SendPropArray3( SENDINFO_ARRAY3( m_iMaxHealth ), SendPropInt( SENDINFO_ARRAY( m_iMaxHealth ), -1, SPROP_UNSIGNED | SPROP_VARINT ) ),
+ SendPropArray3( SENDINFO_ARRAY3( m_iMaxBuffedHealth ), SendPropInt( SENDINFO_ARRAY( m_iMaxBuffedHealth ), -1, SPROP_UNSIGNED | SPROP_VARINT ) ),
+ SendPropArray3( SENDINFO_ARRAY3( m_iPlayerClass ), SendPropInt( SENDINFO_ARRAY( m_iPlayerClass ), 5, SPROP_UNSIGNED ) ),
+ SendPropArray3( SENDINFO_ARRAY3( m_bArenaSpectator ), SendPropBool( SENDINFO_ARRAY( m_bArenaSpectator ) ) ),
+ SendPropArray3( SENDINFO_ARRAY3( m_iActiveDominations ), SendPropInt( SENDINFO_ARRAY( m_iActiveDominations ), 6, SPROP_UNSIGNED ) ),
+ SendPropArray3( SENDINFO_ARRAY3( m_flNextRespawnTime ), SendPropTime( SENDINFO_ARRAY( m_flNextRespawnTime ) ) ),
+ SendPropArray3( SENDINFO_ARRAY3( m_iChargeLevel ), SendPropInt( SENDINFO_ARRAY( m_iChargeLevel ), 8, SPROP_UNSIGNED ) ),
+ SendPropArray3( SENDINFO_ARRAY3( m_iDamage ), SendPropInt( SENDINFO_ARRAY( m_iDamage ), -1, SPROP_UNSIGNED | SPROP_VARINT ) ),
+ SendPropArray3( SENDINFO_ARRAY3( m_iDamageAssist ), SendPropInt( SENDINFO_ARRAY( m_iDamageAssist ), -1, SPROP_UNSIGNED | SPROP_VARINT ) ),
+ SendPropArray3( SENDINFO_ARRAY3( m_iDamageBoss ), SendPropInt( SENDINFO_ARRAY( m_iDamageBoss ), -1, SPROP_UNSIGNED | SPROP_VARINT ) ),
+ SendPropArray3( SENDINFO_ARRAY3( m_iHealing ), SendPropInt( SENDINFO_ARRAY( m_iHealing ), -1, SPROP_UNSIGNED | SPROP_VARINT ) ),
+ SendPropArray3( SENDINFO_ARRAY3( m_iHealingAssist ), SendPropInt( SENDINFO_ARRAY( m_iHealingAssist ), -1, SPROP_UNSIGNED | SPROP_VARINT ) ),
+ SendPropArray3( SENDINFO_ARRAY3( m_iDamageBlocked ), SendPropInt( SENDINFO_ARRAY( m_iDamageBlocked ), -1, SPROP_UNSIGNED | SPROP_VARINT ) ),
+ SendPropArray3( SENDINFO_ARRAY3( m_iCurrencyCollected ), SendPropInt( SENDINFO_ARRAY( m_iCurrencyCollected ), -1, SPROP_UNSIGNED | SPROP_VARINT ) ),
+ SendPropArray3( SENDINFO_ARRAY3( m_iBonusPoints ), SendPropInt( SENDINFO_ARRAY( m_iBonusPoints ), -1, SPROP_UNSIGNED | SPROP_VARINT ) ),
+ SendPropArray3( SENDINFO_ARRAY3( m_iPlayerLevel ), SendPropInt( SENDINFO_ARRAY( m_iPlayerLevel ), -1, SPROP_UNSIGNED | SPROP_VARINT ) ),
+ SendPropArray3( SENDINFO_ARRAY3( m_iStreaks ), SendPropInt( SENDINFO_ARRAY( m_iStreaks ), -1, SPROP_UNSIGNED | SPROP_VARINT ) ),
+ SendPropArray3( SENDINFO_ARRAY3( m_iUpgradeRefundCredits ), SendPropInt( SENDINFO_ARRAY( m_iUpgradeRefundCredits ), -1, SPROP_UNSIGNED | SPROP_VARINT ) ),
+ SendPropArray3( SENDINFO_ARRAY3( m_iBuybackCredits ), SendPropInt( SENDINFO_ARRAY( m_iBuybackCredits ), -1, SPROP_UNSIGNED | SPROP_VARINT ) ),
+ SendPropInt( SENDINFO( m_iPartyLeaderRedTeamIndex ), -1, SPROP_UNSIGNED | SPROP_VARINT ),
+ SendPropInt( SENDINFO( m_iPartyLeaderBlueTeamIndex ), -1, SPROP_UNSIGNED | SPROP_VARINT ),
+ SendPropInt( SENDINFO( m_iEventTeamStatus ), -1, SPROP_UNSIGNED | SPROP_VARINT ),
+ SendPropArray3( SENDINFO_ARRAY3( m_iPlayerClassWhenKilled ), SendPropInt( SENDINFO_ARRAY( m_iPlayerClassWhenKilled ), 5, SPROP_UNSIGNED ) ),
+ SendPropArray3( SENDINFO_ARRAY3( m_iConnectionState ), SendPropInt( SENDINFO_ARRAY( m_iConnectionState ), 3, SPROP_UNSIGNED ) ),
+END_SEND_TABLE()
+
+LINK_ENTITY_TO_CLASS( tf_player_manager, CTFPlayerResource );
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CTFPlayerResource::CTFPlayerResource( void )
+{
+ ListenForGameEvent( "mvm_wave_complete" );
+
+ m_flNextDamageAndHealingSend = 0.f;
+
+ m_iPartyLeaderRedTeamIndex = 0;
+ m_iPartyLeaderBlueTeamIndex = 0;
+ m_iEventTeamStatus = 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFPlayerResource::FireGameEvent( IGameEvent * event )
+{
+ const char *pszEvent = event->GetName();
+
+ if ( !Q_strcmp( pszEvent, "mvm_wave_complete" ) )
+ {
+ // Force a re-send on wave complete
+ m_flNextDamageAndHealingSend = 0.f;
+ UpdatePlayerData();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFPlayerResource::SetPartyLeaderIndex( int iTeam, int iIndex )
+{
+ Assert( iIndex >= 0 && iIndex <= MAX_PLAYERS );
+
+ switch( iTeam )
+ {
+ case TF_TEAM_RED:
+ m_iPartyLeaderRedTeamIndex = iIndex;
+ break;
+ case TF_TEAM_BLUE:
+ m_iPartyLeaderBlueTeamIndex = iIndex;
+ break;
+ default:
+ break;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CTFPlayerResource::GetPartyLeaderIndex( int iTeam )
+{
+ if ( iTeam == TF_TEAM_RED )
+ return m_iPartyLeaderRedTeamIndex;
+ else if ( iTeam == TF_TEAM_BLUE )
+ return m_iPartyLeaderBlueTeamIndex;
+
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFPlayerResource::UpdatePlayerData( void )
+{
+ m_vecRedPlayers.RemoveAll();
+ m_vecBluePlayers.RemoveAll();
+ m_vecFreeSlots.RemoveAll();
+
+ BaseClass::UpdatePlayerData();
+
+ // check if player is still part of the match
+ CMatchInfo *pMatch = GTFGCClientSystem()->GetMatch();
+ if ( pMatch && TFGameRules() )
+ {
+ for ( int i=0; i<pMatch->GetNumTotalMatchPlayers(); ++i )
+ {
+ AssertMsg( m_vecFreeSlots.Count() > 0, "There should always be free slots for player to join" );
+
+ CMatchInfo::PlayerMatchData_t *pData = pMatch->GetMatchDataForPlayer( i );
+ uint32 unAccountID = pData->steamID.GetAccountID();
+ int iTeam = TFGameRules()->GetGameTeamForGCTeam( pData->eGCTeam );
+ CUtlVector< uint32 >* pVecPlayers = iTeam == TF_TEAM_RED ? &m_vecRedPlayers : &m_vecBluePlayers;
+
+ // add players that are not yet connected to the server
+ if ( !pData->bDropped && pVecPlayers->Find( unAccountID ) == pVecPlayers->InvalidIndex() && m_vecFreeSlots.Count() > 0 )
+ {
+ int iIndex = m_vecFreeSlots[0];
+ m_vecFreeSlots.Remove( 0 );
+
+ AssertMsg( m_iAccountID[iIndex] == 0, "No account should be assigned to this slot" );
+
+ Init( iIndex );
+ m_iAccountID.Set( iIndex, unAccountID );
+ m_bValid.Set( iIndex, 1 );
+ m_iTeam.Set( iIndex, iTeam );
+ m_iConnectionState.Set( iIndex, pData->GetConnectionState() );
+ }
+ }
+
+ // do we need to set m_bValid on these?
+ if ( GTFGCClientSystem()->BLateJoinEligible() )
+ {
+ int iTeamSize = pMatch->GetCanonicalMatchSize() / 2;
+
+ int iRedWaiting = iTeamSize-m_vecRedPlayers.Count();
+ for ( int i=0; i<iRedWaiting && m_vecFreeSlots.Count() > 0; ++i )
+ {
+ int iIndex = m_vecFreeSlots[0];
+ m_vecFreeSlots.Remove( 0 );
+
+ AssertMsg( m_iAccountID[iIndex] == 0, "No account should be assigned to this slot" );
+
+ m_iTeam.Set( iIndex, TF_TEAM_RED );
+ m_iConnectionState.Set( iIndex, MM_WAITING_FOR_PLAYER );
+ }
+
+ int iBlueWaiting = iTeamSize-m_vecBluePlayers.Count();
+ for ( int i=0; i<iBlueWaiting && m_vecFreeSlots.Count() > 0; ++i )
+ {
+ int iIndex = m_vecFreeSlots[0];
+ m_vecFreeSlots.Remove( 0 );
+
+ AssertMsg( m_iAccountID[iIndex] == 0, "No account should be assigned to this slot" );
+
+ m_iTeam.Set( iIndex, TF_TEAM_BLUE );
+ m_iConnectionState.Set( iIndex, MM_WAITING_FOR_PLAYER );
+ }
+ }
+ }
+
+ if ( gpGlobals->curtime > m_flNextDamageAndHealingSend )
+ {
+ m_flNextDamageAndHealingSend = gpGlobals->curtime + STATS_SEND_FREQUENCY;
+ }
+
+ if ( pMatch && m_iEventTeamStatus != (int)pMatch->m_unEventTeamStatus )
+ {
+ m_iEventTeamStatus = pMatch->m_unEventTeamStatus;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFPlayerResource::UpdateConnectedPlayer( int iIndex, CBasePlayer *pPlayer )
+{
+ BaseClass::UpdateConnectedPlayer( iIndex, pPlayer );
+
+ CTFPlayer *pTFPlayer = ToTFPlayer( pPlayer );
+
+ PlayerStats_t *pTFPlayerStats = CTF_GameStats.FindPlayerStats( pTFPlayer );
+ if ( pTFPlayerStats )
+ {
+ // Update accumulated and per-round stats
+ localplayerscoring_t *pData = pTFPlayer->m_Shared.GetScoringData();
+ pData->UpdateStats( pTFPlayerStats->statsAccumulated, pTFPlayer, false );
+
+ pData = pTFPlayer->m_Shared.GetRoundScoringData();
+ pData->UpdateStats( pTFPlayerStats->statsCurrentRound, pTFPlayer, true );
+
+ // Send every STATS_SEND_FREQUENCY (1.f)
+ if ( gpGlobals->curtime > m_flNextDamageAndHealingSend )
+ {
+ m_iDamage.Set( iIndex, pTFPlayerStats->statsCurrentRound.m_iStat[TFSTAT_DAMAGE] );
+ m_iDamageAssist.Set( iIndex, pTFPlayerStats->statsCurrentRound.m_iStat[TFSTAT_DAMAGE_ASSIST] );
+ m_iDamageBoss.Set( iIndex, pTFPlayerStats->statsCurrentRound.m_iStat[TFSTAT_DAMAGE_BOSS] );
+ m_iHealing.Set( iIndex, pTFPlayerStats->statsCurrentRound.m_iStat[TFSTAT_HEALING] );
+ m_iHealingAssist.Set( iIndex, pTFPlayerStats->statsCurrentRound.m_iStat[TFSTAT_HEALING_ASSIST] );
+ m_iDamageBlocked.Set( iIndex, pTFPlayerStats->statsCurrentRound.m_iStat[TFSTAT_DAMAGE_BLOCKED] );
+ m_iCurrencyCollected.Set( iIndex, pTFPlayerStats->statsCurrentRound.m_iStat[TFSTAT_CURRENCY_COLLECTED] );
+ m_iBonusPoints.Set( iIndex, pTFPlayerStats->statsCurrentRound.m_iStat[TFSTAT_BONUS_POINTS] );
+ m_iPlayerLevel.Set( iIndex, pTFPlayer->GetExperienceLevel() );
+ }
+ }
+
+ m_iMaxHealth.Set( iIndex, pTFPlayer->GetMaxHealth() );
+
+ // m_iMaxBuffedHealth is misnamed -- it should be m_iMaxHealthForBuffing, but we don't want to change it now due to demos.
+ m_iMaxBuffedHealth.Set( iIndex, pTFPlayer->GetMaxHealthForBuffing() );
+ m_iPlayerClass.Set( iIndex, pTFPlayer->GetPlayerClass()->GetClassIndex() );
+
+ m_iActiveDominations.Set( iIndex, pTFPlayer->GetNumberofDominations() );
+
+ int iTotalScore = CTFGameRules::CalcPlayerScore( &pTFPlayerStats->statsAccumulated, pTFPlayer );
+
+ if ( m_iTotalScore.Get( iIndex ) != iTotalScore )
+ {
+ int nDelta = iTotalScore - m_iTotalScore.Get( iIndex );
+ if ( TFGameRules()->IsMannVsMachineMode() )
+ {
+ MannVsMachineStats_PlayerEvent_PointsChanged( pTFPlayer, nDelta );
+ }
+ else
+ {
+ // Kill eater points-scored tracking. Increment all equipped items with this kill eater type.
+ // We only do this when we're NOT in MvM
+ HatAndMiscEconEntities_OnOwnerKillEaterEventNoParter( pTFPlayer, kKillEaterEvent_PointsScored, nDelta );
+ }
+ }
+
+ m_iTotalScore.Set( iIndex, iTotalScore );
+ m_bArenaSpectator.Set( iIndex, pTFPlayer->IsArenaSpectator() );
+
+ if ( TFGameRules()->IsInTournamentMode() )
+ {
+ float flCharge = pTFPlayer->MedicGetChargeLevel();
+ m_iChargeLevel.Set( iIndex, (int)(flCharge * 100) );
+ }
+ else
+ {
+ m_iChargeLevel.Set( iIndex, 0 );
+ }
+
+ float flRespawnTime = pTFPlayer->IsAlive() ? 0 : TFGameRules()->GetNextRespawnWave( pTFPlayer->GetTeamNumber(), pTFPlayer );
+ if ( pTFPlayer->GetRespawnTimeOverride() != -1.f )
+ {
+ flRespawnTime = pTFPlayer->GetDeathTime() + pTFPlayer->GetRespawnTimeOverride();
+ }
+ m_flNextRespawnTime.Set( iIndex, flRespawnTime );
+
+ for ( int streak_type = 0; streak_type < CTFPlayerShared::kTFStreak_COUNT; streak_type++ )
+ {
+ m_iStreaks.Set( iIndex * CTFPlayerShared::kTFStreak_COUNT + streak_type, pTFPlayer->m_Shared.GetStreak( (CTFPlayerShared::ETFStreak)streak_type ) );
+ }
+
+ if ( g_pPopulationManager )
+ {
+ // Only update when we have new data
+ int nRespecs = g_pPopulationManager->GetNumRespecsAvailableForPlayer( pTFPlayer );
+ m_iUpgradeRefundCredits.Set( iIndex, nRespecs );
+
+ int nBuybacks = g_pPopulationManager->GetNumBuybackCreditsForPlayer( pTFPlayer );
+ m_iBuybackCredits.Set( iIndex, nBuybacks );
+ }
+
+ CSteamID steamID;
+ pTFPlayer->GetSteamID( &steamID );
+
+ int iTeam = pPlayer->GetTeamNumber();
+
+ CMatchInfo *pMatch = GTFGCClientSystem()->GetMatch();
+ if ( pMatch )
+ {
+ CMatchInfo::PlayerMatchData_t *pData = pMatch->GetMatchDataForPlayer( steamID );
+ if ( pData )
+ {
+ iTeam = TFGameRules()->GetGameTeamForGCTeam( pData->eGCTeam );
+ }
+ }
+
+ CUtlVector< uint32 >* pVecPlayers = ( iTeam == TF_TEAM_RED ) ? &m_vecRedPlayers : ( ( iTeam == TF_TEAM_BLUE ) ? &m_vecBluePlayers : NULL );
+ if ( pVecPlayers )
+ {
+ if ( pVecPlayers->Find( steamID.GetAccountID() ) == pVecPlayers->InvalidIndex() )
+ {
+ pVecPlayers->AddToTail( steamID.GetAccountID() );
+ }
+ }
+
+ m_iConnectionState.Set( iIndex, MM_CONNECTED );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFPlayerResource::UpdateDisconnectedPlayer( int iIndex )
+{
+ // cache accountID to see if we should preserve this account
+ uint32 unAccountID = m_iAccountID[iIndex];
+
+ BaseClass::UpdateDisconnectedPlayer( iIndex );
+
+ // preserve if player is still in part of the match, and still gone
+ CMatchInfo *pMatch = GTFGCClientSystem()->GetMatch();
+ if ( pMatch )
+ {
+ CSteamID steamID( unAccountID, GetUniverse(), k_EAccountTypeIndividual );
+ if ( steamID.IsValid() )
+ {
+ CBasePlayer *pPlayer = (CBasePlayer*)UTIL_PlayerBySteamID( steamID );
+ CMatchInfo::PlayerMatchData_t *pData = pMatch->GetMatchDataForPlayer( steamID );
+ // Skip if they're connected
+ if ( pData && ( !pPlayer || !pPlayer->IsConnected() ) )
+ {
+ if ( !pData->bDropped )
+ {
+ int iTeam = TFGameRules()->GetGameTeamForGCTeam( pData->eGCTeam );
+ m_iConnectionState.Set( iIndex, pData->GetConnectionState() );
+ // re-apply the accountID to keep the data
+ m_iAccountID.Set( iIndex, unAccountID );
+ m_iTeam.Set( iIndex, iTeam );
+ m_bValid.Set( iIndex, 1 );
+
+ CUtlVector< uint32 >* pVecPlayers = iTeam == TF_TEAM_RED ? &m_vecRedPlayers : &m_vecBluePlayers;
+ pVecPlayers->AddToTail( unAccountID );
+ return;
+ }
+ }
+ }
+ }
+
+ // free up the slot if we're not preserving it
+ m_iConnectionState.Set( iIndex, MM_DISCONNECTED );
+ m_vecFreeSlots.AddToTail( iIndex );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFPlayerResource::Spawn( void )
+{
+ BaseClass::Spawn();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFPlayerResource::Init( int iIndex )
+{
+ BaseClass::Init( iIndex );
+
+ m_iTotalScore.Set( iIndex, 0 );
+ m_iMaxHealth.Set( iIndex, TF_HEALTH_UNDEFINED );
+ m_iMaxBuffedHealth.Set( iIndex, TF_HEALTH_UNDEFINED );
+ m_iPlayerClass.Set( iIndex, TF_CLASS_UNDEFINED );
+ m_iActiveDominations.Set( iIndex, 0 );
+ m_iPlayerClassWhenKilled.Set( iIndex, TF_CLASS_UNDEFINED );
+ m_iConnectionState.Set( iIndex, MM_DISCONNECTED );
+ m_bValid.Set( iIndex, 0 );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Gets a value from an array member
+//-----------------------------------------------------------------------------
+int CTFPlayerResource::GetTotalScore( int iIndex )
+{
+ Assert( iIndex >= 0 && iIndex <= MAX_PLAYERS );
+
+ CTFPlayer *pPlayer = (CTFPlayer*)UTIL_PlayerByIndex( iIndex );
+
+ if ( pPlayer && pPlayer->IsConnected() )
+ {
+ return m_iTotalScore[iIndex];
+ }
+
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFPlayerResource::SetPlayerClassWhenKilled( int iIndex, int iClass )
+{
+ Assert( iIndex >= 0 && iIndex <= MAX_PLAYERS );
+
+ m_iPlayerClassWhenKilled.Set( iIndex, iClass );
+}
+
+