|
|
//========= 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(); }
|