diff options
Diffstat (limited to 'game/server/basemultiplayerplayer.cpp')
| -rw-r--r-- | game/server/basemultiplayerplayer.cpp | 350 |
1 files changed, 350 insertions, 0 deletions
diff --git a/game/server/basemultiplayerplayer.cpp b/game/server/basemultiplayerplayer.cpp new file mode 100644 index 0000000..fee7515 --- /dev/null +++ b/game/server/basemultiplayerplayer.cpp @@ -0,0 +1,350 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//============================================================================= + +#include "cbase.h" +#include "mp_shareddefs.h" +#include "basemultiplayerplayer.h" + +// Minimum interval between rate-limited commands that players can run. +#define COMMAND_MAX_RATE 0.3 + +CBaseMultiplayerPlayer::CBaseMultiplayerPlayer() +{ + m_iCurrentConcept = MP_CONCEPT_NONE; + m_flLastForcedChangeTeamTime = -1; + m_iBalanceScore = 0; + m_flConnectionTime = gpGlobals->curtime; + + // per life achievement counters + m_pAchievementKV = new KeyValues( "achievement_counts" ); + + m_flAreaCaptureScoreAccumulator = 0.0f; +} + +CBaseMultiplayerPlayer::~CBaseMultiplayerPlayer() +{ + m_pAchievementKV->deleteThis(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CAI_Expresser *CBaseMultiplayerPlayer::CreateExpresser( void ) +{ + m_pExpresser = new CMultiplayer_Expresser(this); + if ( !m_pExpresser) + return NULL; + + m_pExpresser->Connect(this); + return m_pExpresser; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBaseMultiplayerPlayer::PostConstructor( const char *szClassname ) +{ + BaseClass::PostConstructor( szClassname ); + CreateExpresser(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBaseMultiplayerPlayer::ModifyOrAppendCriteria( AI_CriteriaSet& criteriaSet ) +{ + BaseClass::ModifyOrAppendCriteria( criteriaSet ); + + ModifyOrAppendPlayerCriteria( criteriaSet ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CBaseMultiplayerPlayer::SpeakIfAllowed( AIConcept_t concept, const char *modifiers, char *pszOutResponseChosen, size_t bufsize, IRecipientFilter *filter ) +{ + if ( !IsAlive() ) + return false; + + //if ( IsAllowedToSpeak( concept, bRespondingToPlayer ) ) + return Speak( concept, modifiers, pszOutResponseChosen, bufsize, filter ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +IResponseSystem *CBaseMultiplayerPlayer::GetResponseSystem() +{ + return BaseClass::GetResponseSystem(); + // NOTE: This is where you would hook your custom responses. +// return <*>GameRules()->m_ResponseRules[iIndex].m_ResponseSystems[m_iCurrentConcept]; +} + +//----------------------------------------------------------------------------- +// Purpose: Doesn't actually speak the concept. Just finds a response in the system. You then have to play it yourself. +//----------------------------------------------------------------------------- +bool CBaseMultiplayerPlayer::SpeakConcept( AI_Response &response, int iConcept ) +{ + // Save the current concept. + m_iCurrentConcept = iConcept; + return SpeakFindResponse( response, g_pszMPConcepts[iConcept] ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CBaseMultiplayerPlayer::SpeakConceptIfAllowed( int iConcept, const char *modifiers, char *pszOutResponseChosen, size_t bufsize, IRecipientFilter *filter ) +{ + // Save the current concept. + m_iCurrentConcept = iConcept; + return SpeakIfAllowed( g_pszMPConcepts[iConcept], modifiers, pszOutResponseChosen, bufsize, filter ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CBaseMultiplayerPlayer::CanHearAndReadChatFrom( CBasePlayer *pPlayer ) +{ + // can always hear the console unless we're ignoring all chat + if ( !pPlayer ) + return m_iIgnoreGlobalChat != CHAT_IGNORE_ALL; + + // check if we're ignoring all chat + if ( m_iIgnoreGlobalChat == CHAT_IGNORE_ALL ) + return false; + + // check if we're ignoring all but teammates + if ( m_iIgnoreGlobalChat == CHAT_IGNORE_TEAM && g_pGameRules->PlayerRelationship( this, pPlayer ) != GR_TEAMMATE ) + return false; + + // can't hear dead players if we're alive + if ( pPlayer->m_lifeState != LIFE_ALIVE && m_lifeState == LIFE_ALIVE ) + return false; + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CBaseMultiplayerPlayer::ShouldRunRateLimitedCommand( const CCommand &args ) +{ + return ShouldRunRateLimitedCommand( args[0] ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CBaseMultiplayerPlayer::ShouldRunRateLimitedCommand( const char *pszCommand ) +{ + const char *pcmd = pszCommand; + + int i = m_RateLimitLastCommandTimes.Find( pcmd ); + if ( i == m_RateLimitLastCommandTimes.InvalidIndex() ) + { + m_RateLimitLastCommandTimes.Insert( pcmd, gpGlobals->curtime ); + return true; + } + else if ( (gpGlobals->curtime - m_RateLimitLastCommandTimes[i]) < COMMAND_MAX_RATE ) + { + // Too fast. + return false; + } + else + { + m_RateLimitLastCommandTimes[i] = gpGlobals->curtime; + return true; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CBaseMultiplayerPlayer::ClientCommand( const CCommand &args ) +{ + const char *pcmd = args[0]; + + if ( FStrEq( pcmd, "ignoremsg" ) ) + { + if ( ShouldRunRateLimitedCommand( args ) ) + { + m_iIgnoreGlobalChat = (m_iIgnoreGlobalChat + 1) % 3; + switch( m_iIgnoreGlobalChat ) + { + case CHAT_IGNORE_NONE: + ClientPrint( this, HUD_PRINTTALK, "#Accept_All_Messages" ); + break; + case CHAT_IGNORE_ALL: + ClientPrint( this, HUD_PRINTTALK, "#Ignore_Broadcast_Messages" ); + break; + case CHAT_IGNORE_TEAM: + ClientPrint( this, HUD_PRINTTALK, "#Ignore_Broadcast_Team_Messages" ); + break; + default: + break; + } + } + return true; + } + + return BaseClass::ClientCommand( args ); +} + +bool CBaseMultiplayerPlayer::ShouldShowVoiceSubtitleToEnemy( void ) +{ + return false; +} + +//----------------------------------------------------------------------------- +// calculate a score for this player. higher is more likely to be switched +//----------------------------------------------------------------------------- +int CBaseMultiplayerPlayer::CalculateTeamBalanceScore( void ) +{ + // base score is 0 - ( seconds on server ) + float flTimeConnected = gpGlobals->curtime - m_flConnectionTime; + int iScore = 0 - (int)flTimeConnected; + + // if we were switched recently, score us way down + float flLastSwitchedTime = GetLastForcedChangeTeamTime(); + if ( flLastSwitchedTime > 0 && ( gpGlobals->curtime - flLastSwitchedTime ) < 300 ) + { + iScore -= 10000; + } + return iScore; +} + +void CBaseMultiplayerPlayer::Spawn( void ) +{ + ResetPerLifeCounters(); + + StopScoringEscortPoints(); + + BaseClass::Spawn(); +} + +void CBaseMultiplayerPlayer::AwardAchievement( int iAchievement, int iCount ) +{ + Assert( iAchievement >= 0 && iAchievement < 0xFFFF ); // must fit in short + + CSingleUserRecipientFilter filter( this ); + + UserMessageBegin( filter, "AchievementEvent" ); + WRITE_SHORT( iAchievement ); + WRITE_SHORT( iCount ); + MessageEnd(); +} + +#ifdef _DEBUG + + #include "utlbuffer.h" + + void DumpAchievementCounters( const CCommand &args ) + { + int iPlayerIndex = 1; + + if ( args.ArgC() >= 2 ) + { + iPlayerIndex = atoi( args[1] ); + } + + CBaseMultiplayerPlayer *pPlayer = ToBaseMultiplayerPlayer( UTIL_PlayerByIndex( iPlayerIndex ) ); + if ( pPlayer && pPlayer->GetPerLifeCounterKeys() ) + { + CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER ); + pPlayer->GetPerLifeCounterKeys()->RecursiveSaveToFile( buf, 0 ); + + char szBuf[1024]; + + // probably not the best way to print out a CUtlBuffer + int pos = 0; + while ( buf.PeekStringLength() ) + { + szBuf[pos] = buf.GetChar(); + pos++; + } + szBuf[pos] = '\0'; + + Msg( "%s\n", szBuf ); + } + } + ConCommand dump_achievement_counters( "dump_achievement_counters", DumpAchievementCounters, "Spew the per-life achievement counters for multiplayer players", FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY ); + +#endif // _DEBUG + +int CBaseMultiplayerPlayer::GetPerLifeCounterKV( const char *name ) +{ + return m_pAchievementKV->GetInt( name, 0 ); +} + +void CBaseMultiplayerPlayer::SetPerLifeCounterKV( const char *name, int value ) +{ + m_pAchievementKV->SetInt( name, value ); +} + +void CBaseMultiplayerPlayer::ResetPerLifeCounters( void ) +{ + m_pAchievementKV->Clear(); +} + + +ConVar tf_escort_score_rate( "tf_escort_score_rate", "1", FCVAR_CHEAT, "Score for escorting the train, in points per second" ); + +#define ESCORT_SCORE_CONTEXT "AreaScoreContext" +#define ESCORT_SCORE_INTERVAL 0.1 + +//----------------------------------------------------------------------------- +// Purpose: think to accumulate and award points for escorting the train +//----------------------------------------------------------------------------- +void CBaseMultiplayerPlayer::EscortScoringThink( void ) +{ + m_flAreaCaptureScoreAccumulator += ESCORT_SCORE_INTERVAL; + + if ( m_flCapPointScoreRate > 0 ) + { + float flTimeForOnePoint = 1.0f / m_flCapPointScoreRate; + + int iPoints = 0; + + while ( m_flAreaCaptureScoreAccumulator >= flTimeForOnePoint ) + { + m_flAreaCaptureScoreAccumulator -= flTimeForOnePoint; + iPoints++; + } + + if ( iPoints > 0 ) + { + IGameEvent *event = gameeventmanager->CreateEvent( "player_escort_score" ); + if ( event ) + { + event->SetInt( "player", entindex() ); + event->SetInt( "points", iPoints ); + gameeventmanager->FireEvent( event, true /* only to server */ ); + } + } + } + + SetContextThink( &CBaseMultiplayerPlayer::EscortScoringThink, gpGlobals->curtime + ESCORT_SCORE_INTERVAL, ESCORT_SCORE_CONTEXT ); +} + +//----------------------------------------------------------------------------- +// Purpose: We're escorting the train, start giving points +//----------------------------------------------------------------------------- +void CBaseMultiplayerPlayer::StartScoringEscortPoints( float flRate ) +{ + Assert( flRate > 0.0f ); + m_flCapPointScoreRate = flRate; + SetContextThink( &CBaseMultiplayerPlayer::EscortScoringThink, gpGlobals->curtime + ESCORT_SCORE_INTERVAL, ESCORT_SCORE_CONTEXT ); +} + +//----------------------------------------------------------------------------- +// Purpose: Stopped escorting the train +//----------------------------------------------------------------------------- +void CBaseMultiplayerPlayer::StopScoringEscortPoints( void ) +{ + SetContextThink( NULL, 0, ESCORT_SCORE_CONTEXT ); +} + |