Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

468 lines
14 KiB

//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Entities for use in the Robot Destruction TF2 game mode.
//
//=========================================================================//
#include "cbase.h"
#include "tf_logic_player_destruction.h"
#ifdef GAME_DLL
#include "tf_player.h"
#include "entity_capture_flag.h"
#include "tf_obj_dispenser.h"
#include "tf_gamerules.h"
#else
#include "c_tf_player.h"
#endif // GAME_DLL
BEGIN_DATADESC( CPlayerDestructionDispenser )
END_DATADESC()
IMPLEMENT_NETWORKCLASS_ALIASED( PlayerDestructionDispenser, DT_PlayerDestructionDispenser )
LINK_ENTITY_TO_CLASS( pd_dispenser, CPlayerDestructionDispenser );
BEGIN_NETWORK_TABLE( CPlayerDestructionDispenser, DT_PlayerDestructionDispenser )
END_NETWORK_TABLE()
#ifdef GAME_DLL
BEGIN_DATADESC( CTFPlayerDestructionLogic )
DEFINE_KEYFIELD( m_iszPropModelName, FIELD_STRING, "prop_model_name" ),
DEFINE_KEYFIELD( m_iszPropDropSound, FIELD_STRING, "prop_drop_sound" ),
DEFINE_KEYFIELD( m_iszPropPickupSound, FIELD_STRING, "prop_pickup_sound" ),
DEFINE_KEYFIELD( m_nMinPoints, FIELD_INTEGER, "min_points" ),
DEFINE_KEYFIELD( m_nPointsPerPlayer, FIELD_INTEGER, "points_per_player" ),
DEFINE_KEYFIELD( m_nFlagResetDelay, FIELD_INTEGER, "flag_reset_delay" ),
DEFINE_KEYFIELD( m_nHealDistance, FIELD_INTEGER, "heal_distance" ),
DEFINE_INPUTFUNC( FIELD_VOID, "ScoreRedPoints", InputScoreRedPoints ),
DEFINE_INPUTFUNC( FIELD_VOID, "ScoreBluePoints", InputScoreBluePoints ),
DEFINE_INPUTFUNC( FIELD_VOID, "EnableMaxScoreUpdating", InputEnableMaxScoreUpdating ),
DEFINE_INPUTFUNC( FIELD_VOID, "DisableMaxScoreUpdating", InputDisableMaxScoreUpdating ),
DEFINE_INPUTFUNC( FIELD_INTEGER, "SetCountdownTimer", InputSetCountdownTimer ),
DEFINE_INPUTFUNC( FIELD_STRING, "SetCountdownImage", InputSetCountdownImage ),
DEFINE_INPUTFUNC( FIELD_INTEGER, "SetFlagResetDelay", InputSetFlagResetDelay ),
DEFINE_INPUTFUNC( FIELD_INTEGER, "SetPointsOnPlayerDeath", InputSetPointsOnPlayerDeath ),
DEFINE_OUTPUT( m_OnRedScoreChanged, "OnRedScoreChanged" ),
DEFINE_OUTPUT( m_OnBlueScoreChanged, "OnBlueScoreChanged" ),
DEFINE_OUTPUT( m_OnCountdownTimerExpired, "OnCountdownTimerExpired" ),
END_DATADESC()
#endif
LINK_ENTITY_TO_CLASS( tf_logic_player_destruction, CTFPlayerDestructionLogic );
IMPLEMENT_NETWORKCLASS_ALIASED( TFPlayerDestructionLogic, DT_TFPlayerDestructionLogic )
BEGIN_NETWORK_TABLE( CTFPlayerDestructionLogic, DT_TFPlayerDestructionLogic )
#ifdef CLIENT_DLL
RecvPropEHandle( RECVINFO( m_hRedTeamLeader ) ),
RecvPropEHandle( RECVINFO( m_hBlueTeamLeader ) ),
RecvPropString( RECVINFO( m_iszCountdownImage ) ),
RecvPropBool( RECVINFO( m_bUsingCountdownImage ) ),
#else
SendPropEHandle( SENDINFO( m_hRedTeamLeader ) ),
SendPropEHandle( SENDINFO( m_hBlueTeamLeader ) ),
SendPropStringT( SENDINFO( m_iszCountdownImage ) ),
SendPropBool( SENDINFO( m_bUsingCountdownImage ) ),
#endif
END_NETWORK_TABLE()
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CTFPlayerDestructionLogic::CTFPlayerDestructionLogic()
{
#ifdef GAME_DLL
m_iszPropModelName = MAKE_STRING( "models/flag/flag.mdl" );
ListenForGameEvent( "player_disconnect" );
m_bMaxScoreUpdatingAllowed = false;
m_nFlagResetDelay = 60;
m_nHealDistance = 450;
m_nPointsOnPlayerDeath = 1;
#endif // GAME_DLL
m_hRedTeamLeader = NULL;
m_hBlueTeamLeader = NULL;
m_bUsingCountdownImage = false;
#ifdef CLIENT_DLL
m_iszCountdownImage[0] = '\0';
#else
m_iszCountdownImage.Set( NULL_STRING );
#endif
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CTFPlayerDestructionLogic* CTFPlayerDestructionLogic::GetPlayerDestructionLogic()
{
return assert_cast< CTFPlayerDestructionLogic* >( CTFRobotDestructionLogic::GetRobotDestructionLogic() );
}
#ifdef GAME_DLL
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFPlayerDestructionLogic::Precache()
{
BaseClass::Precache();
PrecacheModel( GetPropModelName() );
PrecacheScriptSound( STRING( m_iszPropDropSound ) );
PrecacheScriptSound( STRING( m_iszPropPickupSound ) );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
const char *CTFPlayerDestructionLogic::GetPropModelName() const
{
return STRING( m_iszPropModelName );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFPlayerDestructionLogic::CalcTeamLeader( int iTeam )
{
// team leader's changed team, recalculate team leader for that team
if ( m_hRedTeamLeader.Get() && m_hRedTeamLeader.Get()->GetTeamNumber() != TF_TEAM_RED )
{
m_hRedTeamLeader = NULL;
CalcTeamLeader( TF_TEAM_RED );
}
if ( m_hBlueTeamLeader.Get() && m_hBlueTeamLeader.Get()->GetTeamNumber() != TF_TEAM_BLUE )
{
m_hBlueTeamLeader = NULL;
CalcTeamLeader( TF_TEAM_BLUE );
}
CUtlVector< CTFPlayer * > playerVector;
CollectPlayers( &playerVector, iTeam, COLLECT_ONLY_LIVING_PLAYERS );
CTFPlayer *pTeamLeader = iTeam == TF_TEAM_RED ? m_hRedTeamLeader.Get() : m_hBlueTeamLeader.Get();
int iCurrentLeadingPoint = 0;
if ( pTeamLeader && pTeamLeader->HasItem() )
{
CCaptureFlag *pFlag = dynamic_cast<CCaptureFlag*>( pTeamLeader->GetItem() );
if ( pFlag )
{
iCurrentLeadingPoint = pFlag->GetPointValue();
}
}
else
{
// reset team leader
pTeamLeader = NULL;
if ( iTeam == TF_TEAM_RED )
{
m_hRedTeamLeader = NULL;
UTIL_Remove( m_hRedDispenser );
m_hRedDispenser = NULL;
}
else
{
m_hBlueTeamLeader = NULL;
UTIL_Remove( m_hBlueDispenser );
m_hBlueDispenser = NULL;
}
}
// find new team leader
CTFPlayer *pNewTeamLeader = NULL;
FOR_EACH_VEC( playerVector, i )
{
CTFPlayer *pPlayer = playerVector[i];
if ( pPlayer == pTeamLeader )
continue;
// community request from Watergate author to never have a SPY be the team leader
if ( pPlayer->HasItem() && !pPlayer->IsPlayerClass( TF_CLASS_SPY ) )
{
CCaptureFlag *pFlag = dynamic_cast< CCaptureFlag* >( pPlayer->GetItem() );
if ( pFlag && pFlag->GetPointValue() > iCurrentLeadingPoint )
{
iCurrentLeadingPoint = pFlag->GetPointValue();
pNewTeamLeader = pPlayer;
}
}
}
// set new leader
if ( pNewTeamLeader )
{
CObjectDispenser *pDispenser = NULL;
if ( iTeam == TF_TEAM_RED )
{
m_hRedTeamLeader = pNewTeamLeader;
if ( !m_hRedDispenser )
{
m_hRedDispenser = CreateDispenser( iTeam );
}
pDispenser = m_hRedDispenser;
}
else
{
m_hBlueTeamLeader = pNewTeamLeader;
if ( !m_hBlueDispenser )
{
m_hBlueDispenser = CreateDispenser( iTeam );
}
pDispenser = m_hBlueDispenser;
}
if ( pDispenser )
{
pDispenser->SetOwnerEntity( pNewTeamLeader );
pDispenser->FollowEntity( pNewTeamLeader );
pDispenser->SetBuilder( pNewTeamLeader );
}
}
}
void CTFPlayerDestructionLogic::FireGameEvent( IGameEvent *pEvent )
{
const char* pszName = pEvent->GetName();
if ( FStrEq( pszName, "player_spawn" ) || FStrEq( pszName, "player_disconnect" ) )
{
EvaluatePlayerCount();
return;
}
else if( FStrEq( pszName, "teamplay_pre_round_time_left" ) )
{
// Eat this event so the RD logic doesn't talk
return;
}
BaseClass::FireGameEvent( pEvent );
}
void CTFPlayerDestructionLogic::OnRedScoreChanged()
{
m_OnRedScoreChanged.Set( (float)m_nRedScore / m_nMaxPoints, this, this );
}
void CTFPlayerDestructionLogic::OnBlueScoreChanged()
{
m_OnBlueScoreChanged.Set( (float)m_nBlueScore / m_nMaxPoints, this, this );
}
void CTFPlayerDestructionLogic::EvaluatePlayerCount()
{
// Bail if we're not allowed
if ( !m_bMaxScoreUpdatingAllowed )
return;
CUtlVector< CTFPlayer* > vecAllPlayers;
CollectPlayers( &vecAllPlayers );
m_nMaxPoints = Max( m_nMinPoints, m_nPointsPerPlayer * vecAllPlayers.Count() );
}
void CTFPlayerDestructionLogic::InputScoreRedPoints( inputdata_t& inputdata )
{
ScorePoints( TF_TEAM_RED, 1, SCORE_CORES_COLLECTED, NULL );
}
void CTFPlayerDestructionLogic::InputScoreBluePoints( inputdata_t& inputdata )
{
ScorePoints( TF_TEAM_BLUE, 1, SCORE_CORES_COLLECTED, NULL );
}
void CTFPlayerDestructionLogic::InputEnableMaxScoreUpdating( inputdata_t& inputdata )
{
m_bMaxScoreUpdatingAllowed = true;
EvaluatePlayerCount();
}
void CTFPlayerDestructionLogic::InputDisableMaxScoreUpdating( inputdata_t& inputdata )
{
EvaluatePlayerCount();
m_bMaxScoreUpdatingAllowed = false;
}
void CTFPlayerDestructionLogic::InputSetCountdownTimer( inputdata_t& inputdata )
{
int nTime = inputdata.value.Int();
if ( nTime > 0 )
{
SetCountdownEndTime( gpGlobals->curtime + nTime );
SetThink( &CTFPlayerDestructionLogic::CountdownThink );
SetNextThink( gpGlobals->curtime + 0.05f );
}
else
{
SetCountdownEndTime( -1.f );
SetThink( NULL );
}
}
void CTFPlayerDestructionLogic::CountdownThink( void )
{
if ( m_flCountdownEndTime > -1.f )
{
// if we're done, just reset the end time
if ( m_flCountdownEndTime < gpGlobals->curtime )
{
m_OnCountdownTimerExpired.FireOutput( this, this );
m_flCountdownEndTime = -1.f;
SetThink( NULL );
return;
}
}
SetNextThink( gpGlobals->curtime + 0.05f );
}
void CTFPlayerDestructionLogic::InputSetCountdownImage( inputdata_t& inputdata )
{
m_bUsingCountdownImage = true;
m_iszCountdownImage = inputdata.value.StringID();
}
void CTFPlayerDestructionLogic::InputSetFlagResetDelay( inputdata_t& inputdata )
{
int nDelay = inputdata.value.Int();
if ( nDelay < 0 )
{
nDelay = 0;
}
m_nFlagResetDelay = nDelay;
}
void CTFPlayerDestructionLogic::InputSetPointsOnPlayerDeath( inputdata_t& inputdata )
{
int nPointsOnPlayerDeath = inputdata.value.Int();
if ( nPointsOnPlayerDeath < 0 )
{
nPointsOnPlayerDeath = 0;
}
m_nPointsOnPlayerDeath = nPointsOnPlayerDeath;
}
CObjectDispenser *CTFPlayerDestructionLogic::CreateDispenser( int iTeam )
{
CPlayerDestructionDispenser *pDispenser = static_cast< CPlayerDestructionDispenser* >( CBaseEntity::CreateNoSpawn( "pd_dispenser", vec3_origin, vec3_angle, NULL ) );
pDispenser->ChangeTeam( iTeam );
pDispenser->SetObjectFlags( pDispenser->GetObjectFlags() | OF_DOESNT_HAVE_A_MODEL | OF_PLAYER_DESTRUCTION );
pDispenser->m_iUpgradeLevel = 1;
DispatchSpawn( pDispenser );
pDispenser->FinishedBuilding();
pDispenser->AddEffects( EF_NODRAW );
pDispenser->DisableAmmoPickupSound();
pDispenser->DisableGenerateMetalSound();
pDispenser->m_takedamage = DAMAGE_NO;
CBaseEntity *pTouchTrigger = pDispenser->GetTouchTrigger();
if ( pTouchTrigger )
{
pTouchTrigger->FollowEntity( pDispenser );
}
return pDispenser;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFPlayerDestructionLogic::PlayPropDropSound( CTFPlayer *pPlayer )
{
PlaySound( STRING( m_iszPropDropSound ), pPlayer );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFPlayerDestructionLogic::PlayPropPickupSound( CTFPlayer *pPlayer )
{
PlaySound( STRING( m_iszPropPickupSound ), pPlayer );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFPlayerDestructionLogic::PlaySound( const char *pszSound, CTFPlayer *pPlayer )
{
EmitSound_t params;
params.m_pSoundName = pszSound;
params.m_flSoundTime = 0;
params.m_pflSoundDuration = 0;
params.m_SoundLevel = SNDLVL_70dB;
CPASFilter filter( pPlayer->GetAbsOrigin() );
pPlayer->EmitSound( filter, pPlayer->entindex(), params );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPlayerDestructionDispenser::Spawn( void )
{
// This cast is for the benefit of GCC
m_fObjectFlags |= (int)OF_DOESNT_HAVE_A_MODEL;
m_takedamage = DAMAGE_NO;
m_iUpgradeLevel = 1;
TFGameRules()->OnDispenserBuilt( this );
}
//-----------------------------------------------------------------------------
// Purpose: Finished building
//-----------------------------------------------------------------------------
void CPlayerDestructionDispenser::OnGoActive( void )
{
BaseClass::OnGoActive();
if ( m_hTouchTrigger )
{
m_hTouchTrigger->SetParent( GetParent() );
}
SetModel( "" );
}
//-----------------------------------------------------------------------------
// Spawn the vgui control screens on the object
//-----------------------------------------------------------------------------
void CPlayerDestructionDispenser::GetControlPanelInfo( int nPanelIndex, const char *&pPanelName )
{
// no panels
return;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFPlayerDestructionLogic::TeamWin( int nTeam )
{
if ( TFGameRules() )
{
TFGameRules()->SetWinningTeam( nTeam, WINREASON_PD_POINTS );
}
}
#endif // GAME_DLL
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CTFPlayer *CTFPlayerDestructionLogic::GetTeamLeader( int iTeam ) const
{
return iTeam == TF_TEAM_RED ? m_hRedTeamLeader.Get() : m_hBlueTeamLeader.Get();
}