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.
263 lines
6.1 KiB
263 lines
6.1 KiB
#include "bot_common.h" |
|
|
|
// Follow our leader |
|
|
|
void FollowState::OnEnter(CCSBot *me) |
|
{ |
|
me->StandUp(); |
|
me->Run(); |
|
me->DestroyPath(); |
|
|
|
m_isStopped = false; |
|
m_stoppedTimestamp = 0.0f; |
|
|
|
// to force immediate repath |
|
m_lastLeaderPos.x = -99999999.9f; |
|
m_lastLeaderPos.y = -99999999.9f; |
|
m_lastLeaderPos.z = -99999999.9f; |
|
|
|
m_lastSawLeaderTime = 0; |
|
|
|
// set re-pathing frequency |
|
m_repathInterval.Invalidate(); |
|
|
|
m_isSneaking = false; |
|
|
|
m_walkTime.Invalidate(); |
|
m_isAtWalkSpeed = false; |
|
|
|
m_leaderMotionState = INVALID; |
|
m_idleTimer.Start(RANDOM_FLOAT(2.0f, 5.0f)); |
|
} |
|
|
|
// Determine the leader's motion state by tracking his speed |
|
|
|
void FollowState::ComputeLeaderMotionState(float leaderSpeed) |
|
{ |
|
// walk = 130, run = 250 |
|
const float runWalkThreshold = 140.0f; |
|
const float walkStopThreshold = 10.0f; |
|
LeaderMotionStateType prevState = m_leaderMotionState; |
|
if (leaderSpeed > runWalkThreshold) |
|
{ |
|
m_leaderMotionState = RUNNING; |
|
m_isAtWalkSpeed = false; |
|
} |
|
else if (leaderSpeed > walkStopThreshold) |
|
{ |
|
// track when began to walk |
|
if (!m_isAtWalkSpeed) |
|
{ |
|
m_walkTime.Start(); |
|
m_isAtWalkSpeed = true; |
|
} |
|
|
|
const float minWalkTime = 0.25f; |
|
if (m_walkTime.GetElapsedTime() > minWalkTime) |
|
{ |
|
m_leaderMotionState = WALKING; |
|
} |
|
} |
|
else |
|
{ |
|
m_leaderMotionState = STOPPED; |
|
m_isAtWalkSpeed = false; |
|
} |
|
|
|
// track time spent in this motion state |
|
if (prevState != m_leaderMotionState) |
|
{ |
|
m_leaderMotionStateTime.Start(); |
|
m_waitTime = RANDOM_FLOAT(1.0f, 3.0f); |
|
} |
|
} |
|
|
|
// Follow our leader |
|
// TODO: Clean up this nasty mess |
|
|
|
void FollowState::OnUpdate(CCSBot *me) |
|
{ |
|
// if we lost our leader, give up |
|
if (m_leader == NULL || !m_leader->IsAlive()) |
|
{ |
|
me->Idle(); |
|
return; |
|
} |
|
|
|
// if we are carrying the bomb and at a bombsite, plant |
|
if (me->IsCarryingBomb() && me->IsAtBombsite()) |
|
{ |
|
// plant it |
|
me->SetTask(CCSBot::PLANT_BOMB); |
|
me->PlantBomb(); |
|
|
|
// radio to the team |
|
me->GetChatter()->PlantingTheBomb(me->GetPlace()); |
|
return; |
|
} |
|
|
|
// look around |
|
me->UpdateLookAround(); |
|
|
|
// if we are moving, we are not idle |
|
if (me->IsNotMoving() == false) |
|
m_idleTimer.Start(RANDOM_FLOAT(2.0f, 5.0f)); |
|
|
|
// compute the leader's speed |
|
float leaderSpeed = Vector2D(m_leader->pev->velocity.x, m_leader->pev->velocity.y).Length(); |
|
|
|
// determine our leader's movement state |
|
ComputeLeaderMotionState(leaderSpeed); |
|
|
|
// track whether we can see the leader |
|
bool isLeaderVisible; |
|
if (me->IsVisible(&m_leader->pev->origin)) |
|
{ |
|
m_lastSawLeaderTime = gpGlobals->time; |
|
isLeaderVisible = true; |
|
} |
|
else |
|
{ |
|
isLeaderVisible = false; |
|
} |
|
|
|
// determine whether we should sneak or not |
|
const float farAwayRange = 750.0f; |
|
if ((m_leader->pev->origin - me->pev->origin).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->time - 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 += RANDOM_FLOAT(1.0f, 3.0f); |
|
|
|
// the leader has stopped - if we are close to him, take up a hiding spot |
|
if ((m_leader->pev->origin - me->pev->origin).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) != CCSBot::PROGRESSING) |
|
{ |
|
me->DestroyPath(); |
|
} |
|
|
|
// recompute our path if necessary |
|
if (repath && m_repathInterval.IsElapsed()) |
|
{ |
|
// recompute our path to keep us near our leader |
|
m_lastLeaderPos = m_leader->pev->origin; |
|
|
|
me->ResetStuckMonitor(); |
|
|
|
const float runSpeed = 200.0f; |
|
const float collectRange = (leaderSpeed > runSpeed) ? 600.0f : 400.0f; |
|
FollowTargetCollector collector(m_leader); |
|
SearchSurroundingAreas(TheNavAreaGrid.GetNearestNavArea(&m_lastLeaderPos), &m_lastLeaderPos, collector, collectRange); |
|
|
|
if (cv_bot_debug.value > 0.0f) |
|
{ |
|
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[ RANDOM_LONG(0, collector.m_targetAreaCount - 1) ]; |
|
targetPos = *target->GetCenter(); |
|
me->PrintIfWatched("%4.1f: Bored. Repathing to a new nearby area\n", gpGlobals->time); |
|
} |
|
else |
|
{ |
|
me->PrintIfWatched("%4.1f: Repathing to stay with leader.\n", gpGlobals->time); |
|
|
|
// 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(&me->pev->origin, &close); |
|
|
|
float rangeSq = (me->pev->origin - close).LengthSquared(); |
|
if (rangeSq < closeRangeSq) |
|
{ |
|
target = area; |
|
targetPos = close; |
|
closeRangeSq = rangeSq; |
|
} |
|
} |
|
} |
|
|
|
if (me->ComputePath(target, NULL, FASTEST_ROUTE) == NULL) |
|
me->PrintIfWatched("Pathfind to leader failed.\n"); |
|
|
|
// throttle how often we repath |
|
m_repathInterval.Start(0.5f); |
|
m_idleTimer.Reset(); |
|
} |
|
} |
|
} |
|
|
|
void FollowState::OnExit(CCSBot *me) |
|
{ |
|
; |
|
}
|
|
|