mirror of
https://github.com/s1lentq/ReGameDLL_CS.git
synced 2024-12-29 16:15:43 +03:00
291 lines
7.7 KiB
C++
291 lines
7.7 KiB
C++
/*
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License as published by the
|
|
* Free Software Foundation; either version 2 of the License, or (at
|
|
* your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*
|
|
* In addition, as a special exception, the author gives permission to
|
|
* link the code of this program with the Half-Life Game Engine ("HL
|
|
* Engine") and Modified Game Libraries ("MODs") developed by Valve,
|
|
* L.L.C ("Valve"). You must obey the GNU General Public License in all
|
|
* respects for all of the code used other than the HL Engine and MODs
|
|
* from Valve. If you modify this file, you may extend this exception
|
|
* to your version of the file, but you are not obligated to do so. If
|
|
* you do not wish to do so, delete this exception statement from your
|
|
* version.
|
|
*
|
|
*/
|
|
|
|
#include "precompiled.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 || !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(nullptr, -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 = nullptr;
|
|
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);
|
|
|
|
real_t rangeSq = (me->pev->origin - close).LengthSquared();
|
|
if (rangeSq < closeRangeSq)
|
|
{
|
|
target = area;
|
|
targetPos = close;
|
|
closeRangeSq = rangeSq;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!me->ComputePath(target, nullptr, FASTEST_ROUTE))
|
|
{
|
|
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)
|
|
{
|
|
;
|
|
}
|