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
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 ); |
|
} |
|
} |