//----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTeamControlPoint::HandleScoring( int iTeam ) { if ( TeamplayRoundBasedRules() && !TeamplayRoundBasedRules()->ShouldScorePerRound() ) { GetGlobalTeam( iTeam )->AddScore( 1 ); TeamplayRoundBasedRules()->HandleTeamScoreModify( iTeam, 1 ); CTeamControlPointMaster *pMaster = g_hControlPointMasters.Count() ? g_hControlPointMasters[0] : NULL; if ( pMaster && !pMaster->WouldNewCPOwnerWinGame( this, iTeam ) ) { #ifdef TF_DLL if ( TeamplayRoundBasedRules()->GetGameType() == TF_GAMETYPE_ESCORT ) { CBroadcastRecipientFilter filter; EmitSound( filter, entindex(), "Hud.EndRoundScored" ); } else #endif { CTeamRecipientFilter filter( iTeam ); EmitSound( filter, entindex(), "Hud.EndRoundScored" ); } } } }
//----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTeamControlPoint::CaptureStart( int iCapTeam, int iNumCappingPlayers, int *pCappingPlayers ) { int iNumCappers = iNumCappingPlayers; float flLastOwnershipChangeTime = -1.f; CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, GetControlPointMasterName() ); while( pEnt ) { CTeamControlPointMaster *pMaster = dynamic_cast<CTeamControlPointMaster *>( pEnt ); if ( pMaster && pMaster->IsActive() ) { flLastOwnershipChangeTime = pMaster->GetLastOwnershipChangeTime(); } pEnt = gEntList.FindEntityByClassname( pEnt, GetControlPointMasterName() ); } IGameEvent *event = gameeventmanager->CreateEvent( "teamplay_point_startcapture" ); if ( event ) { event->SetInt( "cp", m_iPointIndex ); event->SetString( "cpname", STRING( m_iszPrintName ) ); event->SetInt( "team", m_iTeam ); event->SetInt( "capteam", iCapTeam ); event->SetFloat( "captime", gpGlobals->curtime - flLastOwnershipChangeTime ); // safety check if ( iNumCappers > 8 ) { iNumCappers = 8; } char cappers[9]; // pCappingPlayers should be max length 8 int i; for( i = 0 ; i < iNumCappers ; i++ ) { cappers[i] = (char)pCappingPlayers[i]; } cappers[i] = '\0'; // pCappingPlayers is a null terminated list of player indices event->SetString( "cappers", cappers ); event->SetInt( "priority", 7 ); gameeventmanager->FireEvent( event ); } }
//----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTeamControlPointRound::FindControlPoints( void ) { // Let out control point masters know that the round has started CTeamControlPointMaster *pMaster = g_hControlPointMasters.Count() ? g_hControlPointMasters[0] : NULL; if ( pMaster ) { // go through all the points CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, pMaster->GetControlPointName() ); while( pEnt ) { CTeamControlPoint *pPoint = assert_cast<CTeamControlPoint *>(pEnt); if ( pPoint ) { const char *pString = STRING( m_iszCPNames ); const char *pName = STRING( pPoint->GetEntityName() ); // HACK to work around a problem with cp_a being returned for an entity name with cp_A const char *pos = Q_stristr( pString, pName ); if ( pos ) { int len = Q_strlen( STRING( pPoint->GetEntityName() ) ); if ( *(pos + len) == ' ' || *(pos + len) == '\0' ) { if( m_ControlPoints.Find( pPoint ) == m_ControlPoints.InvalidIndex() ) { DevMsg( 2, "Adding control point %s to control point round %s\n", pPoint->GetEntityName().ToCStr(), GetEntityName().ToCStr() ); m_ControlPoints.AddToHead( pPoint ); } } } } pEnt = gEntList.FindEntityByClassname( pEnt, pMaster->GetControlPointName() ); } } if( m_ControlPoints.Count() == 0 ) { Warning( "Error! No control points found in map for team_game_round %s!\n", GetEntityName().ToCStr() ); } }
//----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool CTeamControlPointRound::MakePlayable( void ) { CTeamControlPointMaster *pMaster = g_hControlPointMasters.Count() ? g_hControlPointMasters[0] : NULL; if ( pMaster ) { if ( !IsPlayable() ) { // we need to try switching the owners of the teams to make this round playable for ( int i = FIRST_GAME_TEAM ; i < GetNumberOfTeams() ; i++ ) { for ( int j = 0 ; j < m_ControlPoints.Count() ; j++ ) { if ( ( !pMaster->IsBaseControlPoint( m_ControlPoints[j]->GetPointIndex() ) ) && // this is NOT the base point for one of the teams (we don't want to assign the base to the wrong team) ( !WouldNewCPOwnerWinGame( m_ControlPoints[j], i ) ) ) // making this change would make this round playable { // need to find the trigger area associated with this point CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, pMaster->GetTriggerAreaCaptureName() ); while( pEnt ) { CTriggerAreaCapture *pArea = assert_cast<CTriggerAreaCapture*>( pEnt ); if ( pArea ) { if ( pArea->TeamCanCap( i ) ) { CHandle<CTeamControlPoint> hPoint = pArea->GetControlPoint(); if ( hPoint == m_ControlPoints[j] ) { // found! pArea->ForceOwner( i ); // this updates the trigger_area *and* the control_point return true; } } } pEnt = gEntList.FindEntityByClassname( pEnt, pMaster->GetTriggerAreaCaptureName() ); } } } } } } return false; }
//----------------------------------------------------------------------------- // 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; Assert( m_iAreaIndex != -1 ); // 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 ); } } }
//----------------------------------------------------------------------------- // 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: //----------------------------------------------------------------------------- bool CTeamControlPointRound::MakePlayable( void ) { CTeamControlPointMaster *pMaster = g_hControlPointMasters.Count() ? g_hControlPointMasters[0] : NULL; if ( pMaster ) { if ( !IsPlayable() ) { // we need to try switching the owners of the teams to make this round playable for ( int iTeam = FIRST_GAME_TEAM ; iTeam < GetNumberOfTeams() ; iTeam++ ) { for ( int iControlPoint = 0 ; iControlPoint < m_ControlPoints.Count() ; iControlPoint++ ) { if ( ( !pMaster->IsBaseControlPoint( m_ControlPoints[iControlPoint]->GetPointIndex() ) ) && // this is NOT the base point for one of the teams (we don't want to assign the base to the wrong team) ( !WouldNewCPOwnerWinGame( m_ControlPoints[iControlPoint], iTeam ) ) ) // making this change would make this round playable { // need to find the trigger area associated with this point for ( int iObj=0; iObj<ITriggerAreaCaptureAutoList::AutoList().Count(); ++iObj ) { CTriggerAreaCapture *pArea = static_cast< CTriggerAreaCapture * >( ITriggerAreaCaptureAutoList::AutoList()[iObj] ); if ( pArea->TeamCanCap( iTeam ) ) { CHandle<CTeamControlPoint> hPoint = pArea->GetControlPoint(); if ( hPoint == m_ControlPoints[iControlPoint] ) { // found! pArea->ForceOwner( iTeam ); // this updates the trigger_area *and* the control_point return true; } } } } } } } } return false; }
//----------------------------------------------------------------------------- // 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; 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::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: Sets the new owner of the point, plays the appropriate sound and shows the right model //----------------------------------------------------------------------------- void CTeamControlPoint::InternalSetOwner( int iCapTeam, bool bMakeSound, int iNumCappers, int *pCappingPlayers ) { Assert( iCapTeam >= 0 && iCapTeam < GetNumberOfTeams() ); int iOldTeam = m_iTeam; m_iTeam = iCapTeam; ChangeTeam( iCapTeam ); if ( bMakeSound ) { CBroadcastRecipientFilter filter; EmitSound( filter, entindex(), STRING( m_TeamData[m_iTeam].iszCapSound ) ); } // Update visuals SetModel( STRING(m_TeamData[m_iTeam].iszModel) ); SetBodygroup( 0, m_iTeam ); m_nSkin = ( m_iTeam == TEAM_UNASSIGNED ) ? 2 : (m_iTeam - 2); ResetSequence( LookupSequence("idle") ); // We add 1 to the index because we consider the default "no points capped" as 0. TeamplayGameRules()->SetLastCapPointChanged( m_iPointIndex+1 ); // Determine the pose parameters for each team for ( int i = 0; i < m_TeamData.Count(); i++ ) { // Skip spectator if ( i == TEAM_SPECTATOR ) continue; if ( GetModelPtr() && GetModelPtr()->SequencesAvailable() ) { m_TeamData[i].iTeamPoseParam = LookupPoseParameter( UTIL_VarArgs( "cappoint_%d_percentage", i ) ); } else { m_TeamData[i].iTeamPoseParam = -1; } } UpdateCapPercentage(); if ( m_iTeam == TEAM_UNASSIGNED ) { m_OnCapReset.FireOutput( this, this ); } else { // Remap team to get first game team = 1 switch ( m_iTeam - FIRST_GAME_TEAM+1 ) { case 1: m_OnCapTeam1.FireOutput( this, this ); break; case 2: m_OnCapTeam2.FireOutput( this, this ); break; default: Assert(0); break; } } // If we're playing a sound, this is a true cap by players. if ( bMakeSound ) { if ( iOldTeam > LAST_SHARED_TEAM && iOldTeam != m_iTeam ) { // Make the members of our old team say something for ( int i = 1; i <= gpGlobals->maxClients; i++ ) { CBaseMultiplayerPlayer *pPlayer = ToBaseMultiplayerPlayer( UTIL_PlayerByIndex( i ) ); if ( !pPlayer ) continue; if ( pPlayer->GetTeamNumber() == iOldTeam ) { pPlayer->SpeakConceptIfAllowed( MP_CONCEPT_LOST_CONTROL_POINT ); } } } for( int i = 0; i < iNumCappers; i++ ) { int playerIndex = pCappingPlayers[i]; Assert( playerIndex > 0 && playerIndex <= gpGlobals->maxClients ); PlayerCapped( ToBaseMultiplayerPlayer(UTIL_PlayerByIndex( playerIndex )) ); } // Remap team to get first game team = 1 switch ( m_iTeam - FIRST_GAME_TEAM+1 ) { case 1: m_OnOwnerChangedToTeam1.FireOutput( this, this ); break; case 2: m_OnOwnerChangedToTeam2.FireOutput( this, this ); break; } if ( m_iTeam != TEAM_UNASSIGNED && iNumCappers ) { SendCapString( m_iTeam, iNumCappers, pCappingPlayers ); } } // Have control point master check the win conditions now! CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, GetControlPointMasterName() ); while( pEnt ) { CTeamControlPointMaster *pMaster = dynamic_cast<CTeamControlPointMaster *>( pEnt ); if ( pMaster->IsActive() ) { pMaster->CheckWinConditions(); } pEnt = gEntList.FindEntityByClassname( pEnt, GetControlPointMasterName() ); } }
void CTeamTrainWatcher::WatcherThink( void ) { if ( m_bWaitingToRecede ) { if ( m_flRecedeTime < gpGlobals->curtime ) { m_bWaitingToRecede = false; // don't actually recede in overtime if ( TeamplayRoundBasedRules() && !TeamplayRoundBasedRules()->InOvertime() ) { // fire recede output m_OnTrainStartRecede.FireOutput( this, this ); HandleTrainMovement( true ); } } } bool bDisableAlarm = (TeamplayRoundBasedRules() && TeamplayRoundBasedRules()->State_Get() != GR_STATE_RND_RUNNING); if ( bDisableAlarm ) { StopCaptureAlarm(); } // given its next node, we can walk the nodes and find the linear // distance to the next cp node, or to the goal node CFuncTrackTrain *pTrain = m_hTrain; if ( pTrain ) { int iOldTrainSpeedLevel = m_iTrainSpeedLevel; // how fast is the train moving? float flSpeed = pTrain->GetDesiredSpeed(); // divide speed into regions // anything negative is -1 if ( flSpeed < 0 ) { m_iTrainSpeedLevel = -1; // even though our desired speed might be negative, // our actual speed might be zero if we're at a dead end... // this will turn off the < image when the train is done moving backwards if ( pTrain->GetCurrentSpeed() == 0 ) { m_iTrainSpeedLevel = 0; } } else if ( flSpeed > m_flSpeedLevels[2] ) { m_iTrainSpeedLevel = 3; } else if ( flSpeed > m_flSpeedLevels[1] ) { m_iTrainSpeedLevel = 2; } else if ( flSpeed > m_flSpeedLevels[0] ) { m_iTrainSpeedLevel = 1; } else { m_iTrainSpeedLevel = 0; } if ( m_iTrainSpeedLevel != iOldTrainSpeedLevel ) { // make sure the sparks are off if we're not moving backwards anymore if ( m_bHandleTrainMovement ) { if ( m_iTrainSpeedLevel == 0 && iOldTrainSpeedLevel != 0 ) { HandleSparks( false ); } } // play any concepts that we might need to play if ( TeamplayRoundBasedRules() ) { if ( m_iTrainSpeedLevel == 0 && iOldTrainSpeedLevel != 0 ) { TeamplayRoundBasedRules()->HaveAllPlayersSpeakConceptIfAllowed( MP_CONCEPT_CART_STOP ); m_flNextSpeakForwardConceptTime = 0; } else if ( m_iTrainSpeedLevel < 0 && iOldTrainSpeedLevel == 0 ) { TeamplayRoundBasedRules()->HaveAllPlayersSpeakConceptIfAllowed( MP_CONCEPT_CART_MOVING_BACKWARD ); m_flNextSpeakForwardConceptTime = 0; } } } if ( m_iTrainSpeedLevel > 0 && m_flNextSpeakForwardConceptTime < gpGlobals->curtime ) { if ( m_hAreaCap.Get() ) { for ( int i = 1; i <= gpGlobals->maxClients; i++ ) { CBaseMultiplayerPlayer *pPlayer = ToBaseMultiplayerPlayer( UTIL_PlayerByIndex( i ) ); if ( pPlayer ) { if ( m_hAreaCap->IsTouching( pPlayer ) ) { pPlayer->SpeakConceptIfAllowed( MP_CONCEPT_CART_MOVING_FORWARD ); } } } } m_flNextSpeakForwardConceptTime = gpGlobals->curtime + 3.0; } // what percent progress are we at? CPathTrack *pNode = ( pTrain->m_ppath ) ? pTrain->m_ppath->GetNext() : NULL; // if we're moving backwards, GetNext is going to be wrong if ( flSpeed < 0 ) { pNode = pTrain->m_ppath; } if ( pNode ) { float flDistanceToGoal = 0; // distance to next node Vector vecDir = pNode->GetLocalOrigin() - pTrain->GetLocalOrigin(); flDistanceToGoal = vecDir.Length(); // distance of next node to goal node if ( pNode && pNode != m_hGoalNode ) { // walk this until we get to goal node, or a dead end CPathTrack *pPrev = pNode; pNode = pNode->GetNext(); while ( pNode ) { vecDir = pNode->GetLocalOrigin() - pPrev->GetLocalOrigin(); flDistanceToGoal += vecDir.Length(); if ( pNode == m_hGoalNode ) break; pPrev = pNode; pNode = pNode->GetNext(); } } if ( m_flTotalPathDistance <= 0 ) { Assert( !"No path distance in team_train_watcher\n" ); m_flTotalPathDistance = 1; } m_flTotalProgress = clamp( 1.0 - ( flDistanceToGoal / m_flTotalPathDistance ), 0.0, 1.0 ); m_flTrainDistanceFromStart = m_flTotalPathDistance - flDistanceToGoal; // play alert sounds if necessary for ( int iCount = 0 ; iCount < m_iNumCPLinks ; iCount++ ) { if ( m_flTrainDistanceFromStart < m_CPLinks[iCount].flDistanceFromStart - TEAM_TRAIN_ALERT_DISTANCE ) { // back up twice the alert distance before resetting our flag to play the warning again if ( ( m_flTrainDistanceFromStart < m_CPLinks[iCount].flDistanceFromStart - ( TEAM_TRAIN_ALERT_DISTANCE * 2 ) ) || // has receded back twice the alert distance or... ( !m_bTrainCanRecede ) ) // used to catch the case where the train doesn't normally recede but has rolled back down a hill away from the CP { // reset our alert flag m_CPLinks[iCount].bAlertPlayed = false; } } else { if ( m_flTrainDistanceFromStart < m_CPLinks[iCount].flDistanceFromStart && !m_CPLinks[iCount].bAlertPlayed ) { m_CPLinks[iCount].bAlertPlayed = true; bool bFinalPointInMap = false; CTeamControlPoint *pCurrentPoint = m_CPLinks[iCount].hCP.Get(); CTeamControlPointMaster *pMaster = g_hControlPointMasters.Count() ? g_hControlPointMasters[0] : NULL; if ( pMaster ) { // if we're not playing mini-rounds if ( !pMaster->PlayingMiniRounds() ) { for ( int i = FIRST_GAME_TEAM ; i < MAX_CONTROL_POINT_TEAMS ; i++ ) { if ( ObjectiveResource() && ObjectiveResource()->TeamCanCapPoint( pCurrentPoint->GetPointIndex(), i ) ) { if ( pMaster->WouldNewCPOwnerWinGame( pCurrentPoint, i ) ) { bFinalPointInMap = true; } } } } else { // or this is the last round if ( pMaster->NumPlayableControlPointRounds() == 1 ) { CTeamControlPointRound *pRound = pMaster->GetCurrentRound(); if ( pRound ) { for ( int i = FIRST_GAME_TEAM ; i < MAX_CONTROL_POINT_TEAMS ; i++ ) { if ( ObjectiveResource() && ObjectiveResource()->TeamCanCapPoint( pCurrentPoint->GetPointIndex(), i ) ) { if ( pRound->WouldNewCPOwnerWinGame( pCurrentPoint, i ) ) { bFinalPointInMap = true; } } } } } } } PlayCaptureAlert( pCurrentPoint, bFinalPointInMap ); } } } // check to see if we need to start or stop the alarm if ( flDistanceToGoal <= TEAM_TRAIN_ALARM_DISTANCE ) { if ( ObjectiveResource() ) { ObjectiveResource()->SetTrackAlarm( GetTeamNumber(), true ); } if ( !bDisableAlarm ) { if ( !m_pAlarm ) { if ( m_iNumCPLinks > 0 && !m_bAlarmPlayed ) { // start the alarm at the final point StartCaptureAlarm( m_CPLinks[m_iNumCPLinks-1].hCP.Get() ); m_bAlarmPlayed = true; // used to prevent the alarm from starting again on maps where the train doesn't recede (alarm loops for short time then only plays singles) } } else { if ( !m_bTrainCanRecede ) // if the train won't recede, we only want to play the alarm for a short time { if ( m_flAlarmEndTime > 0 && m_flAlarmEndTime < gpGlobals->curtime ) { StopCaptureAlarm(); SetContextThink( &CTeamTrainWatcher::WatcherAlarmThink, gpGlobals->curtime + TW_ALARM_THINK_INTERVAL, TW_ALARM_THINK ); } } } } } else { if ( ObjectiveResource() ) { ObjectiveResource()->SetTrackAlarm( GetTeamNumber(), false ); } StopCaptureAlarm(); m_bAlarmPlayed = false; } } if ( tf_show_train_path.GetBool() ) { CPathTrack *nextNode = NULL; CPathTrack *node = m_hStartNode; CPathTrack::BeginIteration(); while( node ) { node->Visit(); nextNode = node->GetNext(); if ( !nextNode || nextNode->HasBeenVisited() ) break; NDebugOverlay::Line( node->GetAbsOrigin(), nextNode->GetAbsOrigin(), 255, 255, 0, true, NDEBUG_PERSIST_TILL_NEXT_SERVER ); node = nextNode; } CPathTrack::EndIteration(); // show segment of path train is actually on node = pTrain->m_ppath; if ( node && node->GetNext() ) { NDebugOverlay::HorzArrow( node->GetAbsOrigin(), node->GetNext()->GetAbsOrigin(), 5.0f, 255, 0, 0, 255, true, NDEBUG_PERSIST_TILL_NEXT_SERVER ); } } } SetContextThink( &CTeamTrainWatcher::WatcherThink, gpGlobals->curtime + 0.1, TW_THINK ); }