/** * Escape from the bomb. */ void EscapeFromBombState::OnUpdate( CCSBot *me ) { const Vector *bombPos = me->GetGameState()->GetBombPosition(); // if we don't know where the bomb is, we shouldn't be in this state if (bombPos == NULL) { me->Idle(); return; } // grab our knife to move quickly me->EquipKnife(); // look around me->UpdateLookAround(); if (me->UpdatePathMovement() != CCSBot::PROGRESSING) { // we have no path, or reached the end of one - create a new path far away from the bomb FarAwayFromPositionFunctor func( *bombPos ); CNavArea *goalArea = FindMinimumCostArea( me->GetLastKnownArea(), func ); // if this fails, we'll try again next time me->ComputePath( goalArea->GetCenter(), FASTEST_ROUTE ); } }
// Compute shortest path to goal position via A* algorithm // If 'goalArea' is NULL, path will get as close as it can. bool CCSBot::ComputePath(CNavArea *goalArea, const Vector *goal, RouteType route) { // Throttle re-pathing if (!m_repathTimer.IsElapsed()) return false; // randomize to distribute CPU load m_repathTimer.Start(RANDOM_FLOAT(0.4f, 0.6f)); DestroyPath(); CNavArea *startArea = m_lastKnownArea; if (!startArea) return false; // note final specific position Vector pathEndPosition; if (!goal && !goalArea) return false; if (!goal) pathEndPosition = *goalArea->GetCenter(); else pathEndPosition = *goal; // make sure path end position is on the ground if (goalArea) pathEndPosition.z = goalArea->GetZ(&pathEndPosition); else GetGroundHeight(&pathEndPosition, &pathEndPosition.z); // if we are already in the goal area, build trivial path if (startArea == goalArea) { BuildTrivialPath(&pathEndPosition); return true; } // Compute shortest path to goal CNavArea *closestArea = nullptr; PathCost pathCost(this, route); bool pathToGoalExists = NavAreaBuildPath(startArea, goalArea, goal, pathCost, &closestArea); CNavArea *effectiveGoalArea = (pathToGoalExists) ? goalArea : closestArea; // Build path by following parent links // get count int count = 0; CNavArea *area; for (area = effectiveGoalArea; area; area = area->GetParent()) { count++; } // save room for endpoint if (count > MAX_PATH_LENGTH - 1) count = MAX_PATH_LENGTH - 1; if (count == 0) return false; if (count == 1) { BuildTrivialPath(&pathEndPosition); return true; } // build path m_pathLength = count; for (area = effectiveGoalArea; count && area; area = area->GetParent()) { count--; m_path[count].area = area; m_path[count].how = area->GetParentHow(); } // compute path positions if (ComputePathPositions() == false) { PrintIfWatched("Error building path\n"); DestroyPath(); return false; } if (!goal) { switch (m_path[m_pathLength - 1].how) { case GO_NORTH: case GO_SOUTH: pathEndPosition.x = m_path[m_pathLength - 1].pos.x; pathEndPosition.y = effectiveGoalArea->GetCenter()->y; break; case GO_EAST: case GO_WEST: pathEndPosition.x = effectiveGoalArea->GetCenter()->x; pathEndPosition.y = m_path[m_pathLength - 1].pos.y; break; } GetGroundHeight(&pathEndPosition, &pathEndPosition.z); } // append path end position m_path[m_pathLength].area = effectiveGoalArea; m_path[m_pathLength].pos = pathEndPosition; m_path[m_pathLength].ladder = nullptr; m_path[m_pathLength].how = NUM_TRAVERSE_TYPES; m_pathLength++; // do movement setup m_pathIndex = 1; m_areaEnteredTimestamp = gpGlobals->time; m_spotEncounter = nullptr; m_goalPosition = m_path[1].pos; if (m_path[1].ladder) SetupLadderMovement(); else m_pathLadder = nullptr; return true; }
/** * Follow our leader * @todo Clean up this nasty mess */ void FollowState::OnUpdate( CDABot *me ) { // if we lost our leader, give up if (m_leader == NULL || !m_leader->IsAlive()) { me->Idle(); return; } // look around me->UpdateLookAround(); // if we are moving, we are not idle if (me->IsNotMoving() == false) m_idleTimer.Start( RandomFloat( 2.0f, 5.0f ) ); // compute the leader's speed Vector leaderVel = m_leader->GetAbsVelocity(); float leaderSpeed = Vector2D( leaderVel.x, leaderVel.y ).Length(); // determine our leader's movement state ComputeLeaderMotionState( leaderSpeed ); // track whether we can see the leader bool isLeaderVisible; Vector leaderOrigin = GetCentroid( m_leader ); if (me->IsVisible( leaderOrigin )) { m_lastSawLeaderTime = gpGlobals->curtime; isLeaderVisible = true; } else { isLeaderVisible = false; } // determine whether we should sneak or not const float farAwayRange = 750.0f; Vector myOrigin = GetCentroid( me ); if ((leaderOrigin - myOrigin).IsLengthGreaterThan( farAwayRange )) { // far away from leader - run to catch up m_isSneaking = false; } else if (isLeaderVisible) { // if we see leader walking and we are nearby, walk if (m_leaderMotionState == WALKING) m_isSneaking = true; // if we are sneaking and our leader starts running, stop sneaking if (m_isSneaking && m_leaderMotionState == RUNNING) m_isSneaking = false; } // if we haven't seen the leader for a long time, run const float longTime = 20.0f; if (gpGlobals->curtime - m_lastSawLeaderTime > longTime) m_isSneaking = false; if (m_isSneaking) me->Walk(); else me->Run(); bool repath = false; // if the leader has stopped, hide nearby const float nearLeaderRange = 250.0f; if (!me->HasPath() && m_leaderMotionState == STOPPED && m_leaderMotionStateTime.GetElapsedTime() > m_waitTime) { // throttle how often this check occurs m_waitTime += RandomFloat( 1.0f, 3.0f ); // the leader has stopped - if we are close to him, take up a hiding spot if ((leaderOrigin - myOrigin).IsLengthLessThan( nearLeaderRange )) { const float hideRange = 250.0f; if (me->TryToHide( NULL, -1.0f, hideRange, false, USE_NEAREST )) { me->ResetStuckMonitor(); return; } } } // if we have been idle for awhile, move if (m_idleTimer.IsElapsed()) { repath = true; // always walk when we move such a short distance m_isSneaking = true; } // if our leader has moved, repath (don't repath if leading is stopping) if (leaderSpeed > 100.0f && m_leaderMotionState != STOPPED) { repath = true; } // move along our path if (me->UpdatePathMovement( NO_SPEED_CHANGE ) != CDABot::PROGRESSING) { me->DestroyPath(); } // recompute our path if necessary if (repath && m_repathInterval.IsElapsed() && !me->IsOnLadder()) { // recompute our path to keep us near our leader m_lastLeaderPos = leaderOrigin; me->ResetStuckMonitor(); const float runSpeed = 200.0f; const float collectRange = (leaderSpeed > runSpeed) ? 600.0f : 400.0f; // 400, 200 FollowTargetCollector collector( m_leader ); SearchSurroundingAreas( TheNavMesh->GetNearestNavArea( m_lastLeaderPos ), m_lastLeaderPos, collector, collectRange ); if (cv_bot_debug.GetBool()) { for( int i=0; i<collector.m_targetAreaCount; ++i ) collector.m_targetArea[i]->Draw( /*255, 0, 0, 2*/ ); } // move to one of the collected areas if (collector.m_targetAreaCount) { CNavArea *target = NULL; Vector targetPos; // if we are idle, pick a random area if (m_idleTimer.IsElapsed()) { target = collector.m_targetArea[ RandomInt( 0, collector.m_targetAreaCount-1 ) ]; targetPos = target->GetCenter(); me->PrintIfWatched( "%4.1f: Bored. Repathing to a new nearby area\n", gpGlobals->curtime ); } else { me->PrintIfWatched( "%4.1f: Repathing to stay with leader.\n", gpGlobals->curtime ); // find closest area to where we are CNavArea *area; float closeRangeSq = 9999999999.9f; Vector close; for( int a=0; a<collector.m_targetAreaCount; ++a ) { area = collector.m_targetArea[a]; area->GetClosestPointOnArea( myOrigin, &close ); float rangeSq = (myOrigin - close).LengthSqr(); if (rangeSq < closeRangeSq) { target = area; targetPos = close; closeRangeSq = rangeSq; } } } if (target == NULL || me->ComputePath( target->GetCenter(), FASTEST_ROUTE ) == NULL) me->PrintIfWatched( "Pathfind to leader failed.\n" ); // throttle how often we repath m_repathInterval.Start( 0.5f ); m_idleTimer.Reset(); } } }