* Adds support for Visual Studio 2012 and 2013 * VR Mode: . Switches from headtrack.dll to sourcevr.dll . Improved readability of the UI in VR . Removed the IPD calibration tool. TF2 will now obey the Oculus configuration file. Use the Oculus calibration tool in your SDK or install and run "OpenVR" under Tools in Steam to calibrate your IPD. . Added dropdown to enable VR mode in the Video options. Removed the -vr command line option. . Added the ability to switch in and out of VR mode without quitting the game . By default VR mode will run full screen. To switch back to a borderless window set the vr_force_windowed convar. . Added support for VR mode on Linux * Many assorted bug fixes and other changes from Team Fortress in various shared files
350 lines
11 KiB
C++
350 lines
11 KiB
C++
//========= 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.
|
|
//-----------------------------------------------------------------------------
|
|
AI_Response *CBaseMultiplayerPlayer::SpeakConcept( int iConcept )
|
|
{
|
|
m_iCurrentConcept = iConcept;
|
|
return SpeakFindResponse( 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 );
|
|
}
|
|
|