Modified source engine (2017) developed by valve and leaked in 2020. Not for commercial purporses
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.
 
 
 
 
 
 

1232 lines
34 KiB

//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//===========================================================================//
#include "cbase.h"
#include "team_train_watcher.h"
#include "trigger_area_capture.h"
#include "player.h"
#include "teamplay_gamerules.h"
#include "team.h"
#include "team_objectiveresource.h"
#include "team_control_point_master.h"
#include "teamplayroundbased_gamerules.h"
extern ConVar mp_capstyle;
extern ConVar mp_blockstyle;
extern ConVar mp_capdeteriorate_time;
IMPLEMENT_AUTO_LIST( ITriggerAreaCaptureAutoList );
BEGIN_DATADESC(CTriggerAreaCapture)
// Touch functions
DEFINE_FUNCTION( CTriggerAreaCaptureShim::Touch ),
// Think functions
DEFINE_THINKFUNC( CaptureThink ),
// Keyfields
DEFINE_KEYFIELD( m_iszCapPointName, FIELD_STRING, "area_cap_point" ),
DEFINE_KEYFIELD( m_flCapTime, FIELD_FLOAT, "area_time_to_cap" ),
// DEFINE_FIELD( m_iCapMode, FIELD_INTEGER ),
// DEFINE_FIELD( m_bCapturing, FIELD_BOOLEAN ),
// DEFINE_FIELD( m_nCapturingTeam, FIELD_INTEGER ),
// DEFINE_FIELD( m_nOwningTeam, FIELD_INTEGER ),
// DEFINE_FIELD( m_nTeamInZone, FIELD_INTEGER ),
// DEFINE_FIELD( m_fTimeRemaining, FIELD_FLOAT ),
// DEFINE_FIELD( m_flLastReductionTime, FIELD_FLOAT ),
// DEFINE_FIELD( m_bBlocked, FIELD_BOOLEAN ),
// DEFINE_FIELD( m_TeamData, CUtlVector < perteamdata_t > ),
// DEFINE_FIELD( m_Blockers, CUtlVector < blockers_t > ),
// DEFINE_FIELD( m_bActive, FIELD_BOOLEAN ),
// DEFINE_FIELD( m_hPoint, CHandle < CTeamControlPoint > ),
// DEFINE_FIELD( m_bRequiresObject, FIELD_BOOLEAN ),
// DEFINE_FIELD( m_iCapAttemptNumber, FIELD_INTEGER ),
// Inputs
DEFINE_INPUTFUNC( FIELD_VOID, "RoundSpawn", InputRoundSpawn ),
DEFINE_INPUTFUNC( FIELD_STRING, "SetTeamCanCap", InputSetTeamCanCap ),
DEFINE_INPUTFUNC( FIELD_STRING, "SetControlPoint", InputSetControlPoint ),
DEFINE_INPUTFUNC( FIELD_VOID, "CaptureCurrentCP", InputCaptureCurrentCP ),
// Outputs
DEFINE_OUTPUT( m_OnStartTeam1, "OnStartTeam1" ),
DEFINE_OUTPUT( m_OnStartTeam2, "OnStartTeam2" ),
DEFINE_OUTPUT( m_OnBreakTeam1, "OnBreakTeam1" ),
DEFINE_OUTPUT( m_OnBreakTeam2, "OnBreakTeam2" ),
DEFINE_OUTPUT( m_OnCapTeam1, "OnCapTeam1" ),
DEFINE_OUTPUT( m_OnCapTeam2, "OnCapTeam2" ),
DEFINE_OUTPUT( m_StartOutput, "OnStartCap" ),
DEFINE_OUTPUT( m_BreakOutput, "OnBreakCap" ),
DEFINE_OUTPUT( m_CapOutput, "OnEndCap" ),
DEFINE_OUTPUT( m_OnNumCappersChanged, "OnNumCappersChanged" ),
DEFINE_OUTPUT( m_OnNumCappersChanged2, "OnNumCappersChanged2" ),
END_DATADESC();
LINK_ENTITY_TO_CLASS( trigger_capture_area, CTriggerAreaCapture );
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CTriggerAreaCapture::CTriggerAreaCapture()
{
m_TeamData.SetSize( GetNumberOfTeams() );
m_bStartTouch = false;
m_hTrainWatcher = NULL;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTriggerAreaCapture::Spawn( void )
{
BaseClass::Spawn();
AddSpawnFlags( SF_TRIGGER_ALLOW_CLIENTS );
InitTrigger();
Precache();
SetTouch ( &CTriggerAreaCaptureShim::Touch );
SetThink( &CTriggerAreaCapture::CaptureThink );
SetNextThink( gpGlobals->curtime + AREA_THINK_TIME );
for ( int i = 0; i < m_TeamData.Count(); i++ )
{
if ( m_TeamData[i].iNumRequiredToCap < 1 )
{
m_TeamData[i].iNumRequiredToCap = 1;
}
if ( m_TeamData[i].iNumRequiredToStartCap < 1 )
{
m_TeamData[i].iNumRequiredToStartCap = 1;
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CTriggerAreaCapture::KeyValue( const char *szKeyName, const char *szValue )
{
if ( !Q_strncmp( szKeyName, "team_numcap_", 12 ) )
{
int iTeam = atoi(szKeyName+12);
Assert( iTeam >= 0 && iTeam < m_TeamData.Count() );
m_TeamData[iTeam].iNumRequiredToCap = atoi(szValue);
}
else if ( !Q_strncmp( szKeyName, "team_cancap_", 12 ) )
{
int iTeam = atoi(szKeyName+12);
Assert( iTeam >= 0 && iTeam < m_TeamData.Count() );
m_TeamData[iTeam].bCanCap = (atoi(szValue) != 0);
}
else if ( !Q_strncmp( szKeyName, "team_spawn_", 11 ) )
{
int iTeam = atoi(szKeyName+11);
Assert( iTeam >= 0 && iTeam < m_TeamData.Count() );
m_TeamData[iTeam].iSpawnAdjust = atoi(szValue);
}
else if ( !Q_strncmp( szKeyName, "team_startcap_", 14 ) )
{
int iTeam = atoi(szKeyName+14);
Assert( iTeam >= 0 && iTeam < m_TeamData.Count() );
m_TeamData[iTeam].iNumRequiredToStartCap = atoi(szValue);
}
else
{
return BaseClass::KeyValue( szKeyName, szValue );
}
return true;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTriggerAreaCapture::Precache( void )
{
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CTriggerAreaCapture::IsActive( void )
{
return !m_bDisabled;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTriggerAreaCapture::StartTouch(CBaseEntity *pOther)
{
BaseClass::StartTouch( pOther );
if ( PassesTriggerFilters(pOther) && m_hPoint )
{
m_nOwningTeam = m_hPoint->GetOwner();
IGameEvent *event = gameeventmanager->CreateEvent( "controlpoint_starttouch" );
if ( event )
{
event->SetInt( "player", pOther->entindex() );
event->SetInt( "area", m_hPoint->GetPointIndex() );
gameeventmanager->FireEvent( event );
}
// Call capture think immediately to make it update our area's player counts.
// If we don't do this, the player can receive the above event telling him he's
// in a zone, but the objective resource still thinks he's not.
m_bStartTouch = true;
CaptureThink();
m_bStartTouch = false;
if ( m_bCapturing )
{
CTeamControlPointMaster *pMaster = g_hControlPointMasters.Count() ? g_hControlPointMasters[0] : NULL;
if ( pMaster )
{
float flRate = pMaster->GetPartialCapturePointRate();
if ( flRate > 0.0f )
{
CBaseMultiplayerPlayer *pPlayer = ToBaseMultiplayerPlayer(pOther);
if ( pPlayer && pPlayer->GetTeamNumber() == m_nCapturingTeam )
{
pPlayer->StartScoringEscortPoints( flRate );
}
}
}
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTriggerAreaCapture::EndTouch(CBaseEntity *pOther)
{
if ( IsTouching( pOther ) && m_hPoint )
{
IGameEvent *event = gameeventmanager->CreateEvent( "controlpoint_endtouch" );
if ( event )
{
event->SetInt( "player", pOther->entindex() );
event->SetInt( "area", m_hPoint->GetPointIndex() );
gameeventmanager->FireEvent( event );
}
// incase we leave but the area keeps capturing
CBaseMultiplayerPlayer *pPlayer = ToBaseMultiplayerPlayer(pOther);
if ( pPlayer )
{
pPlayer->StopScoringEscortPoints();
}
}
BaseClass::EndTouch( pOther );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CTriggerAreaCapture::CaptureModeScalesWithPlayers() const
{
return mp_capstyle.GetBool();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTriggerAreaCapture::AreaTouch( CBaseEntity *pOther )
{
if ( !IsActive() )
return;
if ( !PassesTriggerFilters(pOther) )
return;
// Don't cap areas unless the round is running
if ( !TeamplayGameRules()->PointsMayBeCaptured() )
return;
// dont touch for non-alive or non-players
if( !pOther->IsPlayer() || !pOther->IsAlive() )
return;
// make sure this point is in the round being played (if we're playing one)
CTeamControlPointMaster *pMaster = g_hControlPointMasters.Count() ? g_hControlPointMasters[0] : NULL;
if ( pMaster && m_hPoint )
{
if ( !pMaster->IsInRound( m_hPoint ) )
{
return;
}
}
if ( m_hPoint )
{
m_nOwningTeam = m_hPoint->GetOwner();
}
CBaseMultiplayerPlayer *pPlayer = ToBaseMultiplayerPlayer(pOther);
Assert( pPlayer );
if ( pPlayer->GetTeamNumber() != m_nOwningTeam )
{
if ( m_TeamData[ pPlayer->GetTeamNumber() ].bCanCap )
{
DisplayCapHintTo( pPlayer );
}
}
}
ConVar mp_simulatemultiplecappers( "mp_simulatemultiplecappers", "1", FCVAR_CHEAT );
#define MAX_CAPTURE_TEAMS 8
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTriggerAreaCapture::CaptureThink( void )
{
SetNextThink( gpGlobals->curtime + AREA_THINK_TIME );
// make sure this point is in the round being played (if we're playing one)
CTeamControlPointMaster *pMaster = g_hControlPointMasters.Count() ? g_hControlPointMasters[0] : NULL;
if ( pMaster && m_hPoint )
{
if ( !pMaster->IsInRound( m_hPoint ) )
{
return;
}
}
if ( !TeamplayGameRules()->PointsMayBeCaptured() )
{
// Points aren't allowed to be captured. If we were
// being captured, we need to clean up and reset.
if ( m_bCapturing )
{
BreakCapture( false );
UpdateNumPlayers();
}
return;
}
// go through our list of players
Assert( GetNumberOfTeams() <= MAX_CAPTURE_TEAMS );
int iNumPlayers[MAX_CAPTURE_TEAMS];
int iNumBlockablePlayers[MAX_CAPTURE_TEAMS]; // Players in the zone who can't cap, but can block / pause caps
CBaseMultiplayerPlayer *pFirstPlayerTouching[MAX_CAPTURE_TEAMS];
for ( int i = FIRST_GAME_TEAM; i < GetNumberOfTeams(); i++ )
{
iNumPlayers[i] = 0;
iNumBlockablePlayers[i] = 0;
pFirstPlayerTouching[i] = NULL;
}
if ( m_hPoint )
{
// Loop through the entities we're touching, and find players
for ( int i = 0; i < m_hTouchingEntities.Count(); i++ )
{
CBaseEntity *ent = m_hTouchingEntities[i];
if ( ent && ent->IsPlayer() )
{
CBaseMultiplayerPlayer *pPlayer = ToBaseMultiplayerPlayer(ent);
if ( pPlayer->IsAlive() )
{
int iTeam = pPlayer->GetTeamNumber();
// If a team's not allowed to cap a point, don't count players in it at all
if ( !TeamplayGameRules()->TeamMayCapturePoint( iTeam, m_hPoint->GetPointIndex() ) )
continue;
if ( !TeamplayGameRules()->PlayerMayCapturePoint( pPlayer, m_hPoint->GetPointIndex() ) )
{
if ( TeamplayGameRules()->PlayerMayBlockPoint( pPlayer, m_hPoint->GetPointIndex() ) )
{
if ( iNumPlayers[iTeam] == 0 && iNumBlockablePlayers[iTeam] == 0 )
{
pFirstPlayerTouching[iTeam] = pPlayer;
}
iNumBlockablePlayers[iTeam] += TeamplayGameRules()->GetCaptureValueForPlayer( pPlayer );
}
continue;
}
if ( iTeam >= FIRST_GAME_TEAM )
{
if ( iNumPlayers[iTeam] == 0 && iNumBlockablePlayers[iTeam] == 0 )
{
pFirstPlayerTouching[iTeam] = pPlayer;
}
iNumPlayers[iTeam] += TeamplayGameRules()->GetCaptureValueForPlayer( pPlayer );
}
}
}
}
}
int iTeamsInZone = 0;
bool bUpdatePlayers = false;
m_nTeamInZone = TEAM_UNASSIGNED;
for ( int i = FIRST_GAME_TEAM; i < GetNumberOfTeams(); i++ )
{
iNumPlayers[i] *= mp_simulatemultiplecappers.GetInt();
if ( m_TeamData[i].iNumTouching != iNumPlayers[i] )
{
m_TeamData[i].iNumTouching = iNumPlayers[i];
bUpdatePlayers = true;
}
m_TeamData[i].iBlockedTouching = m_TeamData[i].iNumTouching;
if ( m_TeamData[i].iNumTouching )
{
iTeamsInZone++;
m_nTeamInZone = i;
}
}
if ( iTeamsInZone > 1 )
{
m_nTeamInZone = TEAM_UNASSIGNED;
}
else
{
// If we've got non-cappable, yet blockable players here for the team that's defending, they
// need to block the cap. This catches cases like the TF invulnerability, which needs to block
// caps, but isn't allowed to contribute to a cap.
for ( int i = FIRST_GAME_TEAM; i < GetNumberOfTeams(); i++ )
{
if ( !iNumBlockablePlayers[i] || m_nTeamInZone == i )
continue;
iTeamsInZone++;
}
}
UpdateTeamInZone();
bool bBlocked = false;
// If the cap is being blocked, reset the number of players so the client
// knows to stop the capture as well.
if ( mp_blockstyle.GetInt() == 1 )
{
if ( m_bCapturing && iTeamsInZone > 1 )
{
bBlocked = true;
for ( int i = FIRST_GAME_TEAM; i < GetNumberOfTeams(); i++ )
{
iNumPlayers[i] = 0;
if ( m_TeamData[i].iNumTouching != iNumPlayers[i] )
{
m_TeamData[i].iNumTouching = iNumPlayers[i];
bUpdatePlayers = true;
}
}
}
}
if ( bUpdatePlayers )
{
UpdateNumPlayers( bBlocked );
}
// When a player blocks, tell them the cap index and attempt number
// only give successive blocks to them if the attempt number is different
if ( m_bCapturing )
{
if ( m_hPoint )
{
m_hPoint->SetLastContestedAt( gpGlobals->curtime );
}
// Calculate the amount of modification to the cap time
float flTimeDelta = gpGlobals->curtime - m_flLastReductionTime;
float flReduction = flTimeDelta;
if ( CaptureModeScalesWithPlayers() )
{
// Diminishing returns for successive players.
for ( int i = 1; i < m_TeamData[m_nTeamInZone].iNumTouching; i++ )
{
flReduction += (flTimeDelta / (float)(i+1));
}
}
m_flLastReductionTime = gpGlobals->curtime;
//if more than one team is in the zone
if( iTeamsInZone > 1 )
{
if ( !m_bBlocked )
{
m_bBlocked = true;
UpdateBlocked();
}
// See if anyone gets credit for the block
float flPercentToGo = m_fTimeRemaining / m_flCapTime;
if ( CaptureModeScalesWithPlayers() )
{
flPercentToGo = m_fTimeRemaining / ((m_flCapTime * 2) * m_TeamData[m_nCapturingTeam].iNumRequiredToCap);
}
if ( ( flPercentToGo <= 0.5 || TeamplayGameRules()->PointsMayAlwaysBeBlocked() ) && m_hPoint )
{
// find the first player that is not on the capturing team
// they have just broken a cap and should be rewarded
// tell the player the capture attempt number, for checking later
CBaseMultiplayerPlayer *pBlockingPlayer = NULL;
for ( int i = FIRST_GAME_TEAM; i < GetNumberOfTeams(); i++ )
{
if ( m_nCapturingTeam == i )
continue;
if ( pFirstPlayerTouching[i] )
{
pBlockingPlayer = pFirstPlayerTouching[i];
break;
}
}
Assert( pBlockingPlayer );
if ( pBlockingPlayer )
{
bool bRepeatBlocker = false;
for ( int i = m_Blockers.Count()-1; i >= 0; i-- )
{
if ( m_Blockers[i].hPlayer != pBlockingPlayer )
continue;
// If this guy's was a blocker, but not valid now, remove him from the list
if ( m_Blockers[i].iCapAttemptNumber != m_iCapAttemptNumber || !IsTouching(m_Blockers[i].hPlayer) ||
( TeamplayGameRules()->PointsMayAlwaysBeBlocked() && m_Blockers[i].flNextBlockTime < gpGlobals->curtime && m_bStartTouch ) )
{
m_Blockers.Remove(i);
continue;
}
bRepeatBlocker = true;
break;
}
if ( !bRepeatBlocker )
{
m_hPoint->CaptureBlocked( pBlockingPlayer );
// Add this guy to our blocker list
int iNew = m_Blockers.AddToTail();
m_Blockers[iNew].hPlayer = pBlockingPlayer;
m_Blockers[iNew].iCapAttemptNumber = m_iCapAttemptNumber;
m_Blockers[iNew].flNextBlockTime = gpGlobals->curtime + 10.0f;
}
}
}
if ( mp_blockstyle.GetInt() == 0 )
{
BreakCapture( false );
}
return;
}
if ( m_bBlocked )
{
m_bBlocked = false;
UpdateBlocked();
}
float flTotalTimeToCap = m_flCapTime;
if ( CaptureModeScalesWithPlayers() )
{
flTotalTimeToCap = ((m_flCapTime * 2) * m_TeamData[m_nCapturingTeam].iNumRequiredToCap);
}
// Now remove the reduction amount after we've determined there's only 1 team in the area
if ( m_nCapturingTeam == m_nTeamInZone )
{
SetCapTimeRemaining( m_fTimeRemaining - flReduction );
}
else if ( m_nOwningTeam == TEAM_UNASSIGNED && m_nTeamInZone != TEAM_UNASSIGNED )
{
SetCapTimeRemaining( m_fTimeRemaining + flReduction );
}
else
{
// Caps deteriorate over time
if ( TeamplayRoundBasedRules() && m_hPoint && TeamplayRoundBasedRules()->TeamMayCapturePoint(m_nCapturingTeam,m_hPoint->GetPointIndex()) )
{
float flDecreaseScale = CaptureModeScalesWithPlayers() ? mp_capdeteriorate_time.GetFloat() : flTotalTimeToCap;
float flDecrease = (flTotalTimeToCap / flDecreaseScale) * flTimeDelta;
if ( TeamplayRoundBasedRules() && TeamplayRoundBasedRules()->InOvertime() )
{
flDecrease *= 6;
}
SetCapTimeRemaining( m_fTimeRemaining + flDecrease );
}
else
{
SetCapTimeRemaining( flTotalTimeToCap );
}
}
/*
//if no-one is in the area
if( iTeamsInZone == 0 )
{
BreakCapture( true );
return;
}
//if they've lost the number of players needed to cap
int iTeamMembersHere = m_TeamData[m_nCapturingTeam].iNumTouching + iNumBlockablePlayers[m_nCapturingTeam];
if ( (iTeamMembersHere == 0 ) || (mp_capstyle.GetInt() == 0 && iTeamMembersHere < m_TeamData[m_nCapturingTeam].iNumRequiredToCap) )
{
BreakCapture( true );
return;
}
*/
// if the cap is done
if ( m_fTimeRemaining <= 0 )
{
EndCapture( m_nCapturingTeam );
return; //we're done
}
else
{
// We may get several simultaneous CaptureThink calls from StartTouch if there are several players on the trigger
// when it is enabled (like in Raid mode). We haven't started reducing m_fTimeRemaining yet but the second call to CaptureThink
// from StartTouch has m_bCapturing set to true and we hit this condition and call BreakCapture right away.
// We put this check here to prevent calling BreakCapture from the StartTouch call to CaptureThink. If the capture should
// really be broken it will happen the next time the trigger thinks on its own.
if ( !m_bStartTouch )
{
if ( m_fTimeRemaining >= flTotalTimeToCap )
{
BreakCapture( false );
return;
}
}
}
}
else
{
// If there are any teams in the zone that aren't the owner, try to start capping
if ( iTeamsInZone > 0 )
{
for ( int i = FIRST_GAME_TEAM; i < GetNumberOfTeams(); i++ )
{
if ( !m_TeamData[i].bCanCap || m_nOwningTeam == i )
continue;
if ( m_TeamData[i].iNumTouching == 0 )
continue;
if ( m_TeamData[i].iNumTouching < m_TeamData[i].iNumRequiredToStartCap )
continue;
if ( !CaptureModeScalesWithPlayers() && m_TeamData[i].iNumTouching < m_TeamData[i].iNumRequiredToCap )
continue;
StartCapture( i, CAPTURE_NORMAL );
break;
}
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTriggerAreaCapture::SetCapTimeRemaining( float flTime )
{
m_fTimeRemaining = flTime;
float flCapPercentage = 0;
if ( m_nCapturingTeam )
{
flCapPercentage = m_fTimeRemaining / m_flCapTime;
if ( CaptureModeScalesWithPlayers() )
{
flCapPercentage = m_fTimeRemaining / ((m_flCapTime * 2) * m_TeamData[m_nCapturingTeam].iNumRequiredToCap);
}
}
ObjectiveResource()->SetCPCapPercentage( m_hPoint->GetPointIndex(), flCapPercentage );
if ( m_hPoint )
{
m_hPoint->UpdateCapPercentage();
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTriggerAreaCapture::SetOwner( int team )
{
//break any current capturing
BreakCapture( false );
HandleRespawnTimeAdjustments( m_nOwningTeam, team );
//set the owner to the passed value
m_nOwningTeam = team;
UpdateOwningTeam();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTriggerAreaCapture::ForceOwner( int team )
{
SetOwner( team );
if ( m_hPoint )
{
m_hPoint->ForceOwner( team );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTriggerAreaCapture::HandleRespawnTimeAdjustments( int oldTeam, int newTeam )
{
if ( oldTeam > LAST_SHARED_TEAM )
{
// reverse the adjust made when the old team captured this point (if we made one)
if ( m_TeamData[oldTeam].iSpawnAdjust != 0 )
{
TeamplayRoundBasedRules()->AddTeamRespawnWaveTime( oldTeam, -m_TeamData[oldTeam].iSpawnAdjust );
}
}
if ( newTeam > LAST_SHARED_TEAM )
{
if ( m_TeamData[newTeam].iSpawnAdjust != 0 )
{
TeamplayRoundBasedRules()->AddTeamRespawnWaveTime( newTeam, m_TeamData[newTeam].iSpawnAdjust );
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTriggerAreaCapture::StartCapture( int team, int capmode )
{
// Remap team to get first game team = 1
switch ( team - FIRST_GAME_TEAM+1 )
{
case 1:
m_OnStartTeam1.FireOutput( this, this );
break;
case 2:
m_OnStartTeam2.FireOutput( this, this );
break;
default:
Assert(0);
break;
}
m_StartOutput.FireOutput(this,this);
m_nCapturingTeam = team;
OnStartCapture( m_nCapturingTeam );
UpdateNumPlayers();
if ( CaptureModeScalesWithPlayers() )
{
SetCapTimeRemaining( ((m_flCapTime * 2) * m_TeamData[team].iNumRequiredToCap) );
}
else
{
SetCapTimeRemaining( m_flCapTime );
}
m_bCapturing = true;
m_bBlocked = false;
m_iCapMode = capmode;
m_flLastReductionTime = gpGlobals->curtime;
UpdateCappingTeam( m_nCapturingTeam );
UpdateBlocked();
if( m_hPoint )
{
int numcappers = 0;
int cappingplayers[MAX_AREA_CAPPERS];
GetNumCappingPlayers( m_nCapturingTeam, numcappers, cappingplayers );
m_hPoint->CaptureStart( m_nCapturingTeam, numcappers, cappingplayers );
}
// tell all touching players to start racking up capture points
CTeamControlPointMaster *pMaster = g_hControlPointMasters.Count() ? g_hControlPointMasters[0] : NULL;
if ( pMaster )
{
float flRate = pMaster->GetPartialCapturePointRate();
if ( flRate > 0.0f )
{
// for each player touch
CTeam *pTeam = GetGlobalTeam( m_nCapturingTeam );
if ( pTeam )
{
for ( int i=0;i<pTeam->GetNumPlayers();i++ )
{
CBaseMultiplayerPlayer *pPlayer = ToBaseMultiplayerPlayer( pTeam->GetPlayer(i) );
if ( pPlayer && IsTouching( pPlayer ) )
{
pPlayer->StartScoringEscortPoints( flRate );
}
}
}
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTriggerAreaCapture::GetNumCappingPlayers( int team, int &numcappers, int *cappingplayers )
{
numcappers = 0;
for ( int i = 1; i <= gpGlobals->maxClients; i++ )
{
CBaseEntity *ent = UTIL_PlayerByIndex( i );
if ( ent )
{
CBaseMultiplayerPlayer *player = ToBaseMultiplayerPlayer(ent);
if ( IsTouching( player ) && ( player->GetTeamNumber() == team ) ) // need to make sure disguised spies aren't included in the list of capping players
{
if ( numcappers < MAX_AREA_CAPPERS-1 )
{
cappingplayers[numcappers] = i;
numcappers++;
}
}
}
}
if ( numcappers < MAX_AREA_CAPPERS )
{
cappingplayers[numcappers] = 0; //null terminate :)
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTriggerAreaCapture::EndCapture( int team )
{
IncrementCapAttemptNumber();
// Remap team to get first game team = 1
switch ( team - FIRST_GAME_TEAM+1 )
{
case 1:
m_OnCapTeam1.FireOutput( this, this );
break;
case 2:
m_OnCapTeam2.FireOutput( this, this );
break;
default:
Assert(0);
break;
}
m_CapOutput.FireOutput(this,this);
int numcappers = 0;
int cappingplayers[MAX_AREA_CAPPERS];
GetNumCappingPlayers( team, numcappers, cappingplayers );
// Handle this before we assign the new team as the owner of this area
HandleRespawnTimeAdjustments( m_nOwningTeam, team );
m_nOwningTeam = team;
m_bCapturing = false;
m_nCapturingTeam = TEAM_UNASSIGNED;
SetCapTimeRemaining( 0 );
//there may have been more than one capper, but only report this one.
//he hasn't gotten points yet, and his name will go in the cap string if its needed
//first capper gets name sent and points given by flag.
//other cappers get points manually above, no name in message
//send the player in the cap string
if( m_hPoint )
{
OnEndCapture( m_nOwningTeam );
UpdateOwningTeam();
m_hPoint->SetOwner( m_nOwningTeam, true, numcappers, cappingplayers );
m_hPoint->CaptureEnd();
}
SetNumCappers( 0 );
// tell all touching players to stop racking up capture points
CTeam *pTeam = GetGlobalTeam( m_nCapturingTeam );
if ( pTeam )
{
for ( int i=0;i<pTeam->GetNumPlayers();i++ )
{
CBaseMultiplayerPlayer *pPlayer = ToBaseMultiplayerPlayer( pTeam->GetPlayer(i) );
if ( pPlayer && IsTouching( pPlayer ) )
{
pPlayer->StopScoringEscortPoints();
}
}
}
// play any special cap sounds
if ( TeamplayRoundBasedRules() )
{
TeamplayRoundBasedRules()->PlaySpecialCapSounds( m_nOwningTeam );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTriggerAreaCapture::BreakCapture( bool bNotEnoughPlayers )
{
if( m_bCapturing )
{
// Remap team to get first game team = 1
switch ( m_nCapturingTeam - FIRST_GAME_TEAM+1 )
{
case 1:
m_OnBreakTeam1.FireOutput( this, this );
break;
case 2:
m_OnBreakTeam2.FireOutput( this, this );
break;
default:
Assert(0);
break;
}
m_BreakOutput.FireOutput(this,this);
m_bCapturing = false;
m_nCapturingTeam = TEAM_UNASSIGNED;
UpdateCappingTeam( TEAM_UNASSIGNED );
if ( bNotEnoughPlayers )
{
IncrementCapAttemptNumber();
}
SetCapTimeRemaining( 0 );
if( m_hPoint )
{
m_hPoint->CaptureEnd();
// The point reverted to it's previous owner.
IGameEvent *event = gameeventmanager->CreateEvent( "teamplay_capture_broken" );
if ( event )
{
event->SetInt( "cp", m_hPoint->GetPointIndex() );
event->SetString( "cpname", m_hPoint->GetName() );
event->SetFloat( "time_remaining", m_fTimeRemaining );
gameeventmanager->FireEvent( event );
}
}
SetNumCappers( 0 );
// tell all touching players to stop racking up capture points
CTeam *pTeam = GetGlobalTeam( m_nCapturingTeam );
if ( pTeam )
{
for ( int i=0;i<pTeam->GetNumPlayers();i++ )
{
CBaseMultiplayerPlayer *pPlayer = ToBaseMultiplayerPlayer( pTeam->GetPlayer(i) );
if ( pPlayer && IsTouching( pPlayer ) )
{
pPlayer->StopScoringEscortPoints();
}
}
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTriggerAreaCapture::IncrementCapAttemptNumber( void )
{
m_iCapAttemptNumber++;
m_Blockers.Purge();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTriggerAreaCapture::InputRoundSpawn( inputdata_t &inputdata )
{
// find the flag we're linked to
if( !m_hPoint )
{
m_hPoint = dynamic_cast<CTeamControlPoint*>( gEntList.FindEntityByName(NULL, STRING(m_iszCapPointName) ) );
if ( m_hPoint )
{
m_nOwningTeam = m_hPoint->GetOwner();
for ( int i = FIRST_GAME_TEAM; i < GetNumberOfTeams(); i++ )
{
m_hPoint->SetCappersRequiredForTeam( i, m_TeamData[i].iNumRequiredToCap );
ObjectiveResource()->SetCPRequiredCappers( m_hPoint->GetPointIndex(), i, m_TeamData[i].iNumRequiredToCap );
ObjectiveResource()->SetTeamCanCap( m_hPoint->GetPointIndex(), i, m_TeamData[i].bCanCap );
if ( CaptureModeScalesWithPlayers() )
{
ObjectiveResource()->SetCPCapTime( m_hPoint->GetPointIndex(), i, (m_flCapTime * 2) * m_TeamData[i].iNumRequiredToCap );
}
else
{
ObjectiveResource()->SetCPCapTime( m_hPoint->GetPointIndex(), i, m_flCapTime );
}
ObjectiveResource()->SetCPCapTimeScalesWithPlayers( m_hPoint->GetPointIndex(), CaptureModeScalesWithPlayers() );
}
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTriggerAreaCapture::InputSetTeamCanCap( inputdata_t &inputdata )
{
// Get the interaction name & target
char parseString[255];
Q_strncpy(parseString, inputdata.value.String(), sizeof(parseString));
char *pszParam = strtok(parseString," ");
if ( pszParam && pszParam[0] )
{
int iTeam = atoi( pszParam );
pszParam = strtok(NULL," ");
if ( pszParam && pszParam[0] )
{
bool bCanCap = (atoi(pszParam) != 0);
if ( iTeam >= 0 && iTeam < GetNumberOfTeams() )
{
m_TeamData[iTeam].bCanCap = bCanCap;
if ( m_hPoint )
{
ObjectiveResource()->SetTeamCanCap( m_hPoint->GetPointIndex(), iTeam, m_TeamData[iTeam].bCanCap );
}
return;
}
}
}
Warning("%s(%s) received SetTeamCanCap input with invalid format. Format should be: <team number> <can cap (0/1)>.\n", GetClassname(), GetDebugName() );
}
void CTriggerAreaCapture::InputCaptureCurrentCP( inputdata_t &inputdata )
{
if ( m_bCapturing )
{
EndCapture( m_nCapturingTeam );
}
}
void CTriggerAreaCapture::InputSetControlPoint( inputdata_t &inputdata )
{
BreakCapture( false ); // clear the capping for the previous point, forces us to recalc on the new one
char parseString[255];
Q_strncpy(parseString, inputdata.value.String(), sizeof(parseString));
m_iszCapPointName = MAKE_STRING( parseString );
m_hPoint = NULL; // force a reset of this
InputRoundSpawn( inputdata );
// force everyone touching to re-touch so the hud gets set up properly
for ( int i = 0; i < m_hTouchingEntities.Count(); i++ )
{
CBaseEntity *ent = m_hTouchingEntities[i];
if ( ent && ent->IsPlayer() )
{
EndTouch( ent );
StartTouch( ent );
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Check if this player's death causes a block
// return FALSE if the player is not in this area
// return TRUE otherwise ( eg player is in area, but his death does not cause break )
//-----------------------------------------------------------------------------
bool CTriggerAreaCapture::CheckIfDeathCausesBlock( CBaseMultiplayerPlayer *pVictim, CBaseMultiplayerPlayer *pKiller )
{
if ( !pVictim || !pKiller )
return false;
// make sure this player is in this area
if ( !IsTouching( pVictim ) )
return false;
// Teamkills shouldn't give a block reward
if ( pVictim->GetTeamNumber() == pKiller->GetTeamNumber() )
return true;
// return if the area is not being capped
if ( !m_bCapturing )
return true;
int iTeam = pVictim->GetTeamNumber();
// return if this player's team is not capping the area
if ( iTeam != m_nCapturingTeam )
return true;
// break early incase we kill multiple people in the same frame
bool bBreakCap = false;
if ( CaptureModeScalesWithPlayers() )
{
bBreakCap = ( m_TeamData[m_nCapturingTeam].iBlockedTouching - 1 ) <= 0;
}
else
{
bBreakCap = ( m_TeamData[m_nCapturingTeam].iBlockedTouching - 1 < m_TeamData[m_nCapturingTeam].iNumRequiredToCap );
}
if ( bBreakCap )
{
m_hPoint->CaptureBlocked( pKiller );
//BreakCapture( true );
}
return true;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTriggerAreaCapture::UpdateNumPlayers( bool bBlocked /*= false */ )
{
if( !m_hPoint )
return;
int index = m_hPoint->GetPointIndex();
for ( int i = 0; i < m_TeamData.Count(); i++ )
{
if ( i >= FIRST_GAME_TEAM && i == m_nCapturingTeam )
{
SetNumCappers( m_TeamData[i].iNumTouching, bBlocked );
}
ObjectiveResource()->SetNumPlayers( index, i, m_TeamData[i].iNumTouching );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTriggerAreaCapture::UpdateOwningTeam( void )
{
if ( m_hPoint )
{
ObjectiveResource()->SetOwningTeam( m_hPoint->GetPointIndex(), m_nOwningTeam );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTriggerAreaCapture::UpdateCappingTeam( int iTeam )
{
if ( m_hPoint )
{
ObjectiveResource()->SetCappingTeam( m_hPoint->GetPointIndex(), iTeam );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTriggerAreaCapture::UpdateTeamInZone( void )
{
if ( m_hPoint )
{
ObjectiveResource()->SetTeamInZone( m_hPoint->GetPointIndex(), m_nTeamInZone );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTriggerAreaCapture::UpdateBlocked( void )
{
if ( m_hPoint )
{
ObjectiveResource()->SetCapBlocked( m_hPoint->GetPointIndex(), m_bBlocked );
m_hPoint->CaptureInterrupted( m_bBlocked );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTriggerAreaCapture::SetNumCappers( int nNumCappers, bool bBlocked /* = false */ )
{
m_OnNumCappersChanged.Set( nNumCappers, this, this );
// m_OnNumCappersChanged2 sets -1 for a blocked cart (for movement decisions on hills)
if ( bBlocked )
{
nNumCappers = -1;
}
m_OnNumCappersChanged2.Set( nNumCappers, this, this );
if ( m_hTrainWatcher.Get() )
{
m_hTrainWatcher->SetNumTrainCappers( nNumCappers, this );
}
}