void CTeamTrainWatcher::WatcherAlarmThink( void ) { CTeamControlPoint *pPoint = m_CPLinks[m_iNumCPLinks-1].hCP.Get(); if ( pPoint ) { pPoint->EmitSound( TEAM_TRAIN_ALARM_SINGLE ); } SetContextThink( &CTeamTrainWatcher::WatcherAlarmThink, gpGlobals->curtime + TW_ALARM_THINK_INTERVAL, TW_ALARM_THINK ); }
//----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- int CTeamControlPointRound::GetPointOwner( int point ) { Assert( point >= 0 ); Assert( point < MAX_CONTROL_POINTS ); CTeamControlPoint *pPoint = m_ControlPoints[point]; if ( pPoint ) return pPoint->GetOwner(); return TEAM_UNASSIGNED; }
CBaseEntity* GetCapturePointByIndex( int iCaptureIndex ) { CTeamControlPoint *pTeamControlPoint = (CTeamControlPoint *)gEntList.FindEntityByClassname( NULL, "team_control_point" ); while ( pTeamControlPoint ) { if ( pTeamControlPoint->GetPointIndex() == iCaptureIndex ) { return pTeamControlPoint; } pTeamControlPoint = (CTeamControlPoint *)gEntList.FindEntityByClassname( pTeamControlPoint, "team_control_point" ); } return NULL; }
//----------------------------------------------------------------------------- // 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: //----------------------------------------------------------------------------- int CTeamControlPoint::GetPreviousPointForTeam( int iGameTeam, int iPrevPoint ) { Assert( iPrevPoint >= 0 && iPrevPoint < MAX_PREVIOUS_POINTS ); int iRetVal = -1; CBaseEntity *pEntity = gEntList.FindEntityByName( NULL, STRING(m_TeamData[iGameTeam].iszPreviousPoint[iPrevPoint]) ); if ( pEntity ) { CTeamControlPoint *pPoint = dynamic_cast<CTeamControlPoint*>( pEntity ); if ( pPoint ) { iRetVal = pPoint->GetPointIndex(); } } return iRetVal; }
ActionResult<CTFBot> CTFBotCapturePoint::Update(CTFBot *actor, float dt) { if (TFGameRules()->InSetup()) { this->m_PathFollower.Invalidate(); this->m_ctRecomputePath.Start(RandomFloat(1.0f, 2.0f)); return ActionResult<CTFBot>::Continue(); } CTeamControlPoint *point = actor->GetMyControlPoint(); if (point == nullptr) { return ActionResult<CTFBot>::SuspendFor(new CTFBotSeekAndDestroy(10.0f), "Seek and destroy until a point becomes available"); } if (point->GetTeamNumber() == actor->GetTeamNumber()) { return ActionResult<CTFBot>::ChangeTo(new CTFBotDefendPoint(), "We need to defend our point(s)"); } const CKnownEntity *threat = actor->GetVisionInterface()->GetPrimaryKnownThreat(false); if (threat != nullptr && threat->IsVisibleRecently()) { actor->EquipBestWeaponForThreat(threat); } if ((!actor->IsPointBeingCaptured(point) || actor->GetTimeSinceWeaponFired() < 2.0f) && !actor->IsCapturingPoint() && !TFGameRules()->InOvertime() && actor->GetTimeLeftToCapture() >= tf_bot_offense_must_push_time.GetFloat() && !TFGameRules()->IsInTraining() && !actor->IsNearPoint(point) && threat != nullptr && threat->IsVisibleRecently()) { float duration = RandomFloat(tf_bot_capture_seek_and_destroy_min_duration.GetFloat(), tf_bot_capture_seek_and_destroy_max_duration.GetFloat()); return ActionResult<CTFBot>::SuspendFor(new CTFBotSeekAndDestroy(duration), "Too early to capture - hunting"); } if (actor->IsCapturingPoint()) { if (point->GetPointIndex() > 7) { return ActionResult<CTFBot>::Continue(); } // TODO // ... // Path::Compute } if (this->m_ctRecomputePath.IsElapsed()) { VPROF_BUDGET("CTFBotCapturePoint::Update( repath )", "NextBot"); CTFBotPathCost cost_func(actor, SAFEST_ROUTE); this->m_PathFollower.Compute(actor, point->GetAbsOrigin(), cost_func, 0.0f, true); this->m_ctRecomputePath.Start(RandomFloat(2.0f, 3.0f)); } if (TFGameRules()->IsInTraining() && !actor->IsAnyPointBeingCaptured() && this->m_PathFollower.GetLength() < 1000.0f) { actor->SpeakConceptIfAllowed(MP_CONCEPT_PLAYER_GO); } else { this->m_PathFollower.Update(actor); } return ActionResult<CTFBot>::Continue(); }
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 ); }