aim: verify camp angles from nav data before using them
aim: tweaked a bit grenade handling, so bots should use them more aim: reduce time between selecting grenade and throwing it away aim: removed hacks in look angles code, due to removing yb_whoose_your_daddy cvar aim: use direct enemy origin from visibility check, and not re-calculate it aim: update enemy prediction, so it now depends on frame interval for a bot aim: additional height offset are tweaked, and now used only for difficulty 4 nav: tweaked a bit player avoidance code, and it's not preventing bot from checking terrain nav: do not check banned nodes, when bucket sizes re too low nav: cover nodes are now selected depending on total bots on server nav: let bot enter pause task after long jump nav: extend velocity by a little for a jump, like it was in first versions of bot nav: stuck checking is now taken in account lower minimal speed if bot is ducking fix: navigation reachability timers, so bots will have correct current node index while camping fix: bots are unable to finish pickup or destroy breakable task, if target is not reachable fix: cover nodes are now calculated as they should fix: manual calling bots add_[t/ct] now ignores yb_join_team cvar bot: tweaked a little difficulty levels, so level 4 is now nightmare level, and 3 is very heard bot: minor refactoring and moving functions to correct source file bot: add yb_economics_disrespect_percent, so bots can ignore economics and buy more different guns bot: add yb_check_darkness that allows to disable darkness checks for bot, thus disallowing usage of flashlight bot: camp buttons are now lightly depends on bot health chat: welcome chat message from bots is now sent during first freeze time period crlib: switch over to stdint.h and remove crlib-own types crlib: fixed alignment in sse code
This commit is contained in:
parent
722e4eda93
commit
29c00565dc
31 changed files with 1395 additions and 1305 deletions
382
src/navigate.cpp
382
src/navigate.cpp
|
|
@ -12,8 +12,8 @@ ConVar cv_path_danger_factor_min ("yb_path_danger_factor_min", "200", "Lower bou
|
|||
ConVar cv_path_danger_factor_max ("yb_path_danger_factor_max", "400", "Upper bound of danger factor that used to add additional danger to path based on practice.", true, 200.0f, 4800.0f);
|
||||
|
||||
int Bot::findBestGoal () {
|
||||
auto pushToHistroy = [&] (int32 goal) -> int32 {
|
||||
m_goalHistory.push (goal);
|
||||
auto pushToHistroy = [&] (int32_t goal) -> int32_t {
|
||||
m_nodeHistory.push (goal);
|
||||
return goal;
|
||||
};
|
||||
|
||||
|
|
@ -188,12 +188,9 @@ int Bot::findBestGoalWhenBombAction () {
|
|||
startTask (Task::Camp, TaskPri::Camp, kInvalidNodeIndex, timeMidBlowup, true); // push camp task on to stack
|
||||
startTask (Task::MoveToPosition, TaskPri::MoveToPosition, result, timeMidBlowup, true); // push move command
|
||||
|
||||
if (path.vis.crouch <= path.vis.stand) {
|
||||
m_campButtons |= IN_DUCK;
|
||||
}
|
||||
else {
|
||||
m_campButtons &= ~IN_DUCK;
|
||||
}
|
||||
// decide to duck or not to duck
|
||||
selectCampButtons (result);
|
||||
|
||||
if (rg.chance (90)) {
|
||||
pushChatterMessage (Chatter::DefendingBombsite);
|
||||
}
|
||||
|
|
@ -259,8 +256,7 @@ int Bot::findGoalPost (int tactic, IntArray *defensive, IntArray *offsensive) {
|
|||
postprocessGoals (graph.m_goalPoints, goalChoices);
|
||||
}
|
||||
}
|
||||
else if (tactic == 4 && !graph.m_rescuePoints.empty ()) // rescue goal
|
||||
{
|
||||
else if (tactic == 4 && !graph.m_rescuePoints.empty ()) {
|
||||
// force ct with hostage(s) to select closest rescue goal
|
||||
float minDist = kInfiniteDistance;
|
||||
int count = 0;
|
||||
|
|
@ -286,7 +282,7 @@ int Bot::findGoalPost (int tactic, IntArray *defensive, IntArray *offsensive) {
|
|||
}
|
||||
|
||||
if (!graph.exists (m_currentNodeIndex)) {
|
||||
m_currentNodeIndex = changePointIndex (findNearestNode ());
|
||||
changeNodeIndex (findNearestNode ());
|
||||
}
|
||||
|
||||
if (goalChoices[0] == kInvalidNodeIndex) {
|
||||
|
|
@ -320,7 +316,7 @@ void Bot::postprocessGoals (const IntArray &goals, int result[]) {
|
|||
for (int index = 0; index < 4; ++index) {
|
||||
auto goal = goals.random ();
|
||||
|
||||
if (searchCount <= 8 && (isBannedNode (goal) || m_prevGoalIndex == goal || ((result[0] == goal || result[1] == goal || result[2] == goal || result[3] == goal) && goals.length () > 4)) && !isOccupiedNode (goal)) {
|
||||
if (searchCount <= 8 && (m_prevGoalIndex == goal || ((result[0] == goal || result[1] == goal || result[2] == goal || result[3] == goal) && goals.length () > 4)) && !isOccupiedNode (goal)) {
|
||||
if (index > 0) {
|
||||
index--;
|
||||
}
|
||||
|
|
@ -370,13 +366,14 @@ void Bot::ignoreCollision () {
|
|||
m_prevOrigin = pev->origin;
|
||||
}
|
||||
|
||||
bool Bot::doPlayerAvoidance (const Vector &normal) {
|
||||
edict_t *hindrance = nullptr;
|
||||
float distance = cr::square (300.0f);
|
||||
void Bot::doPlayerAvoidance (const Vector &normal) {
|
||||
m_hindrance = nullptr;
|
||||
float distance = cr::square (348.0f);
|
||||
|
||||
if (getCurrentTaskId () == Task::Attack || getCurrentTaskId () == Task::SeekCover || isOnLadder ()) {
|
||||
return false;
|
||||
if (getCurrentTaskId () == Task::Attack || isOnLadder ()) {
|
||||
return;
|
||||
}
|
||||
const auto ownPrio = bots.getPlayerPriority (ent ());
|
||||
|
||||
// find nearest player to bot
|
||||
for (const auto &client : util.getClients ()) {
|
||||
|
|
@ -391,28 +388,29 @@ bool Bot::doPlayerAvoidance (const Vector &normal) {
|
|||
continue;
|
||||
}
|
||||
|
||||
// skip if it's not standing
|
||||
if (client.ent->v.flags & FL_DUCKING) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// our team, alive and not myself?
|
||||
if (client.team != m_team || client.ent == ent ()) {
|
||||
continue;
|
||||
}
|
||||
const auto otherPrio = bots.getPlayerPriority (client.ent);
|
||||
|
||||
// give some priorities to bot avoidance
|
||||
if (ownPrio > otherPrio) {
|
||||
continue;
|
||||
}
|
||||
auto nearest = client.ent->v.origin.distanceSq (pev->origin);
|
||||
|
||||
if (nearest < cr::square (pev->maxspeed) && nearest < distance) {
|
||||
hindrance = client.ent;
|
||||
m_hindrance = client.ent;
|
||||
distance = nearest;
|
||||
}
|
||||
}
|
||||
|
||||
// found somebody?
|
||||
if (!hindrance) {
|
||||
return false;
|
||||
if (game.isNullEntity (m_hindrance)) {
|
||||
return;
|
||||
}
|
||||
const float interval = getFrameInterval () * 4.0f;
|
||||
const float interval = m_frameInterval * 7.25f;
|
||||
|
||||
// use our movement angles, try to predict where we should be next frame
|
||||
Vector right, forward;
|
||||
|
|
@ -423,44 +421,39 @@ bool Bot::doPlayerAvoidance (const Vector &normal) {
|
|||
predict += right * m_strafeSpeed * interval;
|
||||
predict += pev->velocity * interval;
|
||||
|
||||
auto movedDistance = hindrance->v.origin.distanceSq (predict);
|
||||
auto nextFrameDistance = pev->origin.distanceSq (hindrance->v.origin + hindrance->v.velocity * interval);
|
||||
auto movedDistance = m_hindrance->v.origin.distanceSq (predict);
|
||||
auto nextFrameDistance = pev->origin.distanceSq (m_hindrance->v.origin + m_hindrance->v.velocity * interval);
|
||||
|
||||
// is player that near now or in future that we need to steer away?
|
||||
if (movedDistance <= cr::square (48.0f) || (distance <= cr::square (56.0f) && nextFrameDistance < distance)) {
|
||||
auto dir = (pev->origin - hindrance->v.origin).normalize2d ();
|
||||
auto dir = (pev->origin - m_hindrance->v.origin).normalize2d ();
|
||||
|
||||
// to start strafing, we have to first figure out if the target is on the left side or right side
|
||||
if ((dir | right.get2d ()) > 0.0f) {
|
||||
if ((dir | right.normalize2d ()) > 0.0f) {
|
||||
setStrafeSpeed (normal, pev->maxspeed);
|
||||
}
|
||||
else {
|
||||
setStrafeSpeed (normal, -pev->maxspeed);
|
||||
}
|
||||
if (distance < cr::square (56.0f)) {
|
||||
if ((dir | forward.get2d ()) < 0.0f) {
|
||||
|
||||
if (distance < cr::square (76.0f)) {
|
||||
if ((dir | forward.normalize2d ()) < 0.0f) {
|
||||
m_moveSpeed = -pev->maxspeed;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Bot::checkTerrain (float movedDistance, const Vector &dirNormal) {
|
||||
|
||||
// if avoiding someone do not consider stuck
|
||||
TraceResult tr {};
|
||||
m_isStuck = doPlayerAvoidance (dirNormal);
|
||||
float checkSpeed = isDucking () ? 4.0f : 10.0f;
|
||||
|
||||
if (m_isStuck) {
|
||||
resetCollision ();
|
||||
return;
|
||||
}
|
||||
m_isStuck = false;
|
||||
|
||||
// Standing still, no need to check?
|
||||
// FIXME: doesn't care for ladder movement (handled separately) should be included in some way
|
||||
if ((m_moveSpeed >= 10.0f || m_strafeSpeed >= 10.0f) && m_lastCollTime < game.time () && m_seeEnemyTime + 0.8f < game.time () && getCurrentTaskId () != Task::Attack) {
|
||||
// standing still, no need to check?
|
||||
if ((m_moveSpeed >= checkSpeed || m_strafeSpeed >= checkSpeed) && m_lastCollTime < game.time ()) {
|
||||
|
||||
// didn't we move enough previously?
|
||||
if (movedDistance < 2.0f && m_prevSpeed >= 20.0f) {
|
||||
|
|
@ -504,7 +497,7 @@ void Bot::checkTerrain (float movedDistance, const Vector &dirNormal) {
|
|||
|
||||
// bot is stuc, but not yet decided what to do?
|
||||
if (m_collisionState == CollisionState::Undecided) {
|
||||
uint32 bits = 0;
|
||||
uint32_t bits = 0;
|
||||
|
||||
if (isOnLadder ()) {
|
||||
bits |= CollisionProbe::Strafe;
|
||||
|
|
@ -515,7 +508,7 @@ void Bot::checkTerrain (float movedDistance, const Vector &dirNormal) {
|
|||
|
||||
// collision check allowed if not flying through the air
|
||||
if (isOnFloor () || isOnLadder () || isInWater ()) {
|
||||
uint32 state[kMaxCollideMoves * 2 + 1] {};
|
||||
uint32_t state[kMaxCollideMoves * 2 + 1] {};
|
||||
int i = 0;
|
||||
|
||||
Vector src {}, dst {};
|
||||
|
|
@ -623,7 +616,7 @@ void Bot::checkTerrain (float movedDistance, const Vector &dirNormal) {
|
|||
}
|
||||
}
|
||||
}
|
||||
if (pev->flags & FL_DUCKING) {
|
||||
if (isDucking ()) {
|
||||
src = pev->origin;
|
||||
}
|
||||
else {
|
||||
|
|
@ -724,13 +717,105 @@ void Bot::checkTerrain (float movedDistance, const Vector &dirNormal) {
|
|||
}
|
||||
}
|
||||
|
||||
void Bot::moveToGoal () {
|
||||
findValidNode ();
|
||||
|
||||
bool prevLadder = false;
|
||||
|
||||
if (graph.exists (m_previousNodes[0])) {
|
||||
if (graph[m_previousNodes[0]].flags & NodeFlag::Ladder) {
|
||||
prevLadder = true;
|
||||
}
|
||||
}
|
||||
|
||||
// press duck button if we need to
|
||||
if ((m_pathFlags & NodeFlag::Crouch) && !(m_pathFlags & (NodeFlag::Camp | NodeFlag::Goal))) {
|
||||
pev->button |= IN_DUCK;
|
||||
}
|
||||
m_lastUsedNodesTime = game.time ();
|
||||
|
||||
// press jump button if we need to leave the ladder
|
||||
if (!(m_pathFlags & NodeFlag::Ladder) && prevLadder && isOnFloor () && isOnLadder () && m_moveSpeed > 50.0f && pev->velocity.length () < 50.0f) {
|
||||
pev->button |= IN_JUMP;
|
||||
m_jumpTime = game.time () + 1.0f;
|
||||
}
|
||||
|
||||
if (m_pathFlags & NodeFlag::Ladder) {
|
||||
if (m_pathOrigin.z < pev->origin.z + 16.0f && !isOnLadder () && isOnFloor () && !isDucking ()) {
|
||||
if (!prevLadder) {
|
||||
m_moveSpeed = pev->origin.distance (m_pathOrigin);
|
||||
}
|
||||
else {
|
||||
m_moveSpeed = 150.0f;
|
||||
}
|
||||
if (m_moveSpeed < 150.0f) {
|
||||
m_moveSpeed = 150.0f;
|
||||
}
|
||||
else if (m_moveSpeed > pev->maxspeed) {
|
||||
m_moveSpeed = pev->maxspeed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// special movement for swimming here
|
||||
if (isInWater ()) {
|
||||
// check if we need to go forward or back press the correct buttons
|
||||
if (isInFOV (m_destOrigin - getEyesPos ()) > 90.0f) {
|
||||
pev->button |= IN_BACK;
|
||||
}
|
||||
else {
|
||||
pev->button |= IN_FORWARD;
|
||||
}
|
||||
|
||||
if (m_moveAngles.x > 60.0f) {
|
||||
pev->button |= IN_DUCK;
|
||||
}
|
||||
else if (m_moveAngles.x < -60.0f) {
|
||||
pev->button |= IN_JUMP;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Bot::translateInput () {
|
||||
if (m_duckTime >= game.time ()) {
|
||||
pev->button |= IN_DUCK;
|
||||
}
|
||||
|
||||
if (pev->button & IN_JUMP) {
|
||||
m_jumpTime = game.time ();
|
||||
}
|
||||
|
||||
if (m_jumpTime + 0.85f > game.time ()) {
|
||||
if (!isOnFloor () && !isInWater () && !isOnLadder ()) {
|
||||
pev->button |= IN_DUCK;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(pev->button & (IN_FORWARD | IN_BACK))) {
|
||||
if (m_moveSpeed > 0.0f) {
|
||||
pev->button |= IN_FORWARD;
|
||||
}
|
||||
else if (m_moveSpeed < 0.0f) {
|
||||
pev->button |= IN_BACK;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(pev->button & (IN_MOVELEFT | IN_MOVERIGHT))) {
|
||||
if (m_strafeSpeed > 0.0f) {
|
||||
pev->button |= IN_MOVERIGHT;
|
||||
}
|
||||
else if (m_strafeSpeed < 0.0f) {
|
||||
pev->button |= IN_MOVELEFT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Bot::updateNavigation () {
|
||||
// this function is a main path navigation
|
||||
|
||||
// check if we need to find a node...
|
||||
if (m_currentNodeIndex == kInvalidNodeIndex) {
|
||||
findValidNode ();
|
||||
m_pathOrigin = m_path->origin;
|
||||
|
||||
// if graph node radius non zero vary origin a bit depending on the body angles
|
||||
if (m_path->radius > 0.0f) {
|
||||
|
|
@ -749,7 +834,9 @@ bool Bot::updateNavigation () {
|
|||
// if bot's on the ground or on the ladder we're free to jump. actually setting the correct velocity is cheating.
|
||||
// pressing the jump button gives the illusion of the bot actual jumping.
|
||||
if (isOnFloor () || isOnLadder ()) {
|
||||
pev->velocity = m_desiredVelocity;
|
||||
if (m_desiredVelocity.length2d () > 0.0f) {
|
||||
pev->velocity = m_desiredVelocity + m_desiredVelocity * 0.05f;
|
||||
}
|
||||
pev->button |= IN_JUMP;
|
||||
|
||||
m_jumpFinished = true;
|
||||
|
|
@ -759,10 +846,16 @@ bool Bot::updateNavigation () {
|
|||
}
|
||||
else if (!cv_jasonmode.bool_ () && usesKnife () && isOnFloor ()) {
|
||||
selectBestWeapon ();
|
||||
|
||||
// if jump distance was big enough, cooldown a little
|
||||
if (m_jumpDistance > 180.0f) {
|
||||
startTask (Task::Pause, TaskPri::Pause, kInvalidNodeIndex, game.time () + 0.45f, false);
|
||||
}
|
||||
m_jumpDistance = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_path->flags & NodeFlag::Ladder) {
|
||||
if (m_pathFlags & NodeFlag::Ladder) {
|
||||
if (!m_pathWalk.empty ()) {
|
||||
if (m_pathWalk.hasNext ()) {
|
||||
if (graph[m_pathWalk.next ()].flags & NodeFlag::Ladder || isOnLadder ()) {
|
||||
|
|
@ -776,10 +869,10 @@ bool Bot::updateNavigation () {
|
|||
m_pathOrigin.z += pev->origin.z - m_pathOrigin.z;
|
||||
}
|
||||
if (m_pathOrigin.z > (pev->origin.z + 16.0f)) {
|
||||
m_pathOrigin = m_path->origin - Vector (0.0f, 0.0f, 16.0f);
|
||||
m_pathOrigin = m_pathOrigin - Vector (0.0f, 0.0f, 16.0f);
|
||||
}
|
||||
if (m_pathOrigin.z < (pev->origin.z - 16.0f)) {
|
||||
m_pathOrigin = m_path->origin + Vector (0.0f, 0.0f, 16.0f);
|
||||
m_pathOrigin = m_pathOrigin + Vector (0.0f, 0.0f, 16.0f);
|
||||
}
|
||||
}
|
||||
m_destOrigin = m_pathOrigin;
|
||||
|
|
@ -813,7 +906,7 @@ bool Bot::updateNavigation () {
|
|||
}
|
||||
}
|
||||
else {
|
||||
game.testHull (getEyesPos (), m_path->origin, TraceIgnore::Monsters, pev->flags & FL_DUCKING ? head_hull : human_hull, ent (), &tr);
|
||||
game.testHull (getEyesPos (), m_pathOrigin, TraceIgnore::Monsters, isDucking () ? head_hull : human_hull, ent (), &tr);
|
||||
|
||||
// someone is above or below us and is using the ladder already
|
||||
if (tr.pHit == client.ent && cr::abs (pev->origin.z - client.ent->v.origin.z) > 15.0f && (client.ent->v.movetype == MOVETYPE_FLY)) {
|
||||
|
|
@ -843,7 +936,7 @@ bool Bot::updateNavigation () {
|
|||
}
|
||||
if (foundGround) {
|
||||
if (getCurrentTaskId () != Task::MoveToPosition || !cr::fequal (getTask ()->desire, TaskPri::PlantBomb)) {
|
||||
m_currentNodeIndex = m_previousNodes[0];
|
||||
changeNodeIndex (m_previousNodes[0]);
|
||||
startTask (Task::MoveToPosition, TaskPri::PlantBomb, previousNode, 0.0f, true);
|
||||
}
|
||||
break;
|
||||
|
|
@ -856,7 +949,7 @@ bool Bot::updateNavigation () {
|
|||
}
|
||||
|
||||
// special lift handling (code merged from podbotmm)
|
||||
if (m_path->flags & NodeFlag::Lift) {
|
||||
if (m_pathFlags & NodeFlag::Lift) {
|
||||
if (updateLiftHandling ()) {
|
||||
if (!updateLiftStates ()) {
|
||||
return false;
|
||||
|
|
@ -929,16 +1022,16 @@ bool Bot::updateNavigation () {
|
|||
}
|
||||
|
||||
float desiredDistance = 8.0f;
|
||||
float nodeDistance = pev->origin.distance2d (m_pathOrigin);
|
||||
float nodeDistance = pev->origin.distance (m_pathOrigin);
|
||||
|
||||
// initialize the radius for a special node type, where the node is considered to be reached
|
||||
if (m_path->flags & NodeFlag::Lift) {
|
||||
if (m_pathFlags & NodeFlag::Lift) {
|
||||
desiredDistance = 50.0f;
|
||||
}
|
||||
else if ((pev->flags & FL_DUCKING) || (m_path->flags & NodeFlag::Goal)) {
|
||||
else if (isDucking () || (m_pathFlags & NodeFlag::Goal)) {
|
||||
desiredDistance = 25.0f;
|
||||
}
|
||||
else if (m_path->flags & NodeFlag::Ladder) {
|
||||
else if (m_pathFlags & NodeFlag::Ladder) {
|
||||
desiredDistance = 24.0f;
|
||||
}
|
||||
else if (m_currentTravelFlags & PathFlag::Jump) {
|
||||
|
|
@ -963,7 +1056,7 @@ bool Bot::updateNavigation () {
|
|||
}
|
||||
|
||||
// needs precise placement - check if we get past the point
|
||||
if (desiredDistance < 22.0f && nodeDistance < 30.0f && m_pathOrigin.distanceSq (pev->origin + pev->velocity * getFrameInterval ()) >= cr::square (nodeDistance)) {
|
||||
if (desiredDistance < 22.0f && nodeDistance < 30.0f && m_pathOrigin.distanceSq (pev->origin + pev->velocity * m_frameInterval) >= cr::square (nodeDistance)) {
|
||||
desiredDistance = nodeDistance + 1.0f;
|
||||
}
|
||||
|
||||
|
|
@ -1048,7 +1141,7 @@ bool Bot::updateLiftHandling () {
|
|||
};
|
||||
|
||||
// trace line to door
|
||||
game.testLine (pev->origin, m_path->origin, TraceIgnore::Everything, ent (), &tr);
|
||||
game.testLine (pev->origin, m_pathOrigin, TraceIgnore::Everything, ent (), &tr);
|
||||
|
||||
if (tr.flFraction < 1.0f && tr.pHit && strcmp (tr.pHit->v.classname.chars (), "func_door") == 0 && (m_liftState == LiftState::None || m_liftState == LiftState::WaitingFor || m_liftState == LiftState::LookingButtonOutside) && pev->groundentity != tr.pHit) {
|
||||
if (m_liftState == LiftState::None) {
|
||||
|
|
@ -1059,7 +1152,7 @@ bool Bot::updateLiftHandling () {
|
|||
}
|
||||
|
||||
// trace line down
|
||||
game.testLine (m_path->origin, m_path->origin + Vector (0.0f, 0.0f, -50.0f), TraceIgnore::Everything, ent (), &tr);
|
||||
game.testLine (m_path->origin, m_pathOrigin + Vector (0.0f, 0.0f, -50.0f), TraceIgnore::Everything, ent (), &tr);
|
||||
|
||||
// if trace result shows us that it is a lift
|
||||
if (!game.isNullEntity (tr.pHit) && !m_pathWalk.empty () && (strcmp (tr.pHit->v.classname.chars (), "func_door") == 0 || strcmp (tr.pHit->v.classname.chars (), "func_plat") == 0 || strcmp (tr.pHit->v.classname.chars (), "func_train") == 0) && !liftClosedDoorExists) {
|
||||
|
|
@ -1067,7 +1160,7 @@ bool Bot::updateLiftHandling () {
|
|||
if (cr::abs (pev->origin.z - tr.vecEndPos.z) < 70.0f) {
|
||||
m_liftEntity = tr.pHit;
|
||||
m_liftState = LiftState::EnteringIn;
|
||||
m_liftTravelPos = m_path->origin;
|
||||
m_liftTravelPos = m_pathOrigin;
|
||||
m_liftUsageTime = game.time () + 5.0f;
|
||||
}
|
||||
}
|
||||
|
|
@ -1289,7 +1382,7 @@ bool Bot::updateLiftHandling () {
|
|||
m_liftUsageTime = 0.0f;
|
||||
|
||||
clearSearchNodes ();
|
||||
findBestNearestNode ();
|
||||
findNextBestNode ();
|
||||
|
||||
if (graph.exists (m_previousNodes[2])) {
|
||||
findPath (m_currentNodeIndex, m_previousNodes[2], FindPath::Fast);
|
||||
|
|
@ -1302,7 +1395,7 @@ bool Bot::updateLiftHandling () {
|
|||
}
|
||||
|
||||
bool Bot::updateLiftStates () {
|
||||
if (!game.isNullEntity (m_liftEntity) && !(m_path->flags & NodeFlag::Lift)) {
|
||||
if (!game.isNullEntity (m_liftEntity) && !(m_pathFlags & NodeFlag::Lift)) {
|
||||
if (m_liftState == LiftState::TravelingBy) {
|
||||
m_liftState = LiftState::Leaving;
|
||||
m_liftUsageTime = game.time () + 10.0f;
|
||||
|
|
@ -1324,14 +1417,14 @@ bool Bot::updateLiftStates () {
|
|||
|
||||
if (graph.exists (m_previousNodes[0])) {
|
||||
if (!(graph[m_previousNodes[0]].flags & NodeFlag::Lift)) {
|
||||
changePointIndex (m_previousNodes[0]);
|
||||
changeNodeIndex (m_previousNodes[0]);
|
||||
}
|
||||
else {
|
||||
findBestNearestNode ();
|
||||
findNextBestNode ();
|
||||
}
|
||||
}
|
||||
else {
|
||||
findBestNearestNode ();
|
||||
findNextBestNode ();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
@ -1630,9 +1723,9 @@ void Bot::findPath (int srcIndex, int destIndex, FindPath pathType /*= FindPath:
|
|||
auto childRoute = &m_routes[child.index];
|
||||
|
||||
// calculate the F value as F = G + H
|
||||
float g = curRoute->g + gcalc (m_team, child.index, currentIndex);
|
||||
float h = hcalc (child.index, srcIndex, destIndex);
|
||||
float f = g + h;
|
||||
const float g = curRoute->g + gcalc (m_team, child.index, currentIndex);
|
||||
const float h = hcalc (child.index, srcIndex, destIndex);
|
||||
const float f = g + h;
|
||||
|
||||
if (childRoute->state == RouteState::New || childRoute->f > f) {
|
||||
// put the current child into open list
|
||||
|
|
@ -1674,7 +1767,7 @@ int Bot::findAimingNode (const Vector &to, int &pathLength) {
|
|||
// return the most distant node which is seen from the bot to the target and is within count
|
||||
|
||||
if (m_currentNodeIndex == kInvalidNodeIndex) {
|
||||
m_currentNodeIndex = changePointIndex (findNearestNode ());
|
||||
changeNodeIndex (findNearestNode ());
|
||||
}
|
||||
|
||||
int destIndex = graph.getNearest (to);
|
||||
|
|
@ -1704,7 +1797,7 @@ int Bot::findAimingNode (const Vector &to, int &pathLength) {
|
|||
return bestIndex;
|
||||
}
|
||||
|
||||
bool Bot::findBestNearestNode () {
|
||||
bool Bot::findNextBestNode () {
|
||||
// this function find a node in the near of the bot if bot had lost his path of pathfinder needs
|
||||
// to be restarted over again.
|
||||
|
||||
|
|
@ -1714,7 +1807,7 @@ bool Bot::findBestNearestNode () {
|
|||
int lessIndex[3] = { kInvalidNodeIndex, kInvalidNodeIndex , kInvalidNodeIndex };
|
||||
|
||||
auto &bucket = graph.getNodesInBucket (pev->origin);
|
||||
int numToSkip = cr::clamp (rg.get (0, 3), 0, static_cast <int> (bucket.length () / 2));
|
||||
int numToSkip = cr::clamp (rg.get (0, 2), 0, static_cast <int> (bucket.length () / 2));
|
||||
|
||||
for (const int at : bucket) {
|
||||
bool skip = !!(at == m_currentNodeIndex);
|
||||
|
|
@ -1748,7 +1841,7 @@ bool Bot::findBestNearestNode () {
|
|||
}
|
||||
|
||||
// ignore non-reacheable nodes...
|
||||
if (!isReachableNode (at) || isBannedNode (at)) {
|
||||
if (!isReachableNode (at)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -1809,25 +1902,14 @@ bool Bot::findBestNearestNode () {
|
|||
if (selected == kInvalidNodeIndex) {
|
||||
selected = findNearestNode ();
|
||||
}
|
||||
m_goalHistory.push (selected);
|
||||
changePointIndex (selected);
|
||||
m_nodeHistory.push (selected);
|
||||
changeNodeIndex (selected);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
float Bot::getReachTime () {
|
||||
auto task = getCurrentTaskId ();
|
||||
float estimatedTime = 0.0f;
|
||||
|
||||
switch (task) {
|
||||
case Task::Pause:
|
||||
case Task::Camp:
|
||||
case Task::Hide:
|
||||
return 0.0f;
|
||||
|
||||
default:
|
||||
estimatedTime = 2.8f; // time to reach next node
|
||||
}
|
||||
float Bot::getEstimatedNodeReachTime () {
|
||||
float estimatedTime = 2.8f;
|
||||
|
||||
// calculate 'real' time that we need to get from one node to another
|
||||
if (graph.exists (m_currentNodeIndex) && graph.exists (m_previousNodes[0])) {
|
||||
|
|
@ -1840,7 +1922,7 @@ float Bot::getReachTime () {
|
|||
else {
|
||||
estimatedTime = 3.0f * distance / pev->maxspeed;
|
||||
}
|
||||
bool longTermReachability = (m_path->flags & NodeFlag::Crouch) || (m_path->flags & NodeFlag::Ladder) || (pev->button & IN_DUCK) || (m_oldButtons & IN_DUCK);
|
||||
bool longTermReachability = (m_pathFlags & NodeFlag::Crouch) || (m_pathFlags & NodeFlag::Ladder) || (pev->button & IN_DUCK) || (m_oldButtons & IN_DUCK);
|
||||
|
||||
// check for special nodes, that can slowdown our movement
|
||||
if (longTermReachability) {
|
||||
|
|
@ -1848,7 +1930,7 @@ float Bot::getReachTime () {
|
|||
}
|
||||
estimatedTime = cr::clamp (estimatedTime, 2.0f, longTermReachability ? 8.0f : 5.0f);
|
||||
}
|
||||
return estimatedTime;
|
||||
return estimatedTime + m_frameInterval;
|
||||
}
|
||||
|
||||
void Bot::findValidNode () {
|
||||
|
|
@ -1857,11 +1939,9 @@ void Bot::findValidNode () {
|
|||
// if bot hasn't got a node we need a new one anyway or if time to get there expired get new one as well
|
||||
if (m_currentNodeIndex == kInvalidNodeIndex) {
|
||||
clearSearchNodes ();
|
||||
findBestNearestNode ();
|
||||
|
||||
m_pathOrigin = m_path->origin;
|
||||
findNextBestNode ();
|
||||
}
|
||||
else if (m_navTimeset + getReachTime () < game.time () && game.isNullEntity (m_enemy)) {
|
||||
else if (m_navTimeset + getEstimatedNodeReachTime () < game.time () && game.isNullEntity (m_enemy)) {
|
||||
|
||||
// increase danager for both teams
|
||||
for (int team = Team::Terrorist; team < kGameTeamNum; ++team) {
|
||||
|
|
@ -1880,13 +1960,11 @@ void Bot::findValidNode () {
|
|||
graph.setDangerDamage (m_team, m_currentNodeIndex, m_currentNodeIndex, damageValue);
|
||||
}
|
||||
clearSearchNodes ();
|
||||
findBestNearestNode ();
|
||||
|
||||
m_pathOrigin = m_path->origin;
|
||||
findNextBestNode ();
|
||||
}
|
||||
}
|
||||
|
||||
int Bot::changePointIndex (int index) {
|
||||
int Bot::changeNodeIndex (int index) {
|
||||
if (index == kInvalidNodeIndex) {
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1896,11 +1974,14 @@ int Bot::changePointIndex (int index) {
|
|||
m_previousNodes[0] = m_currentNodeIndex;
|
||||
|
||||
m_currentNodeIndex = index;
|
||||
|
||||
m_navTimeset = game.time ();
|
||||
m_collideTime = game.time ();
|
||||
|
||||
m_path = &graph[index];
|
||||
m_path = &graph[m_currentNodeIndex];
|
||||
m_pathFlags = m_path->flags;
|
||||
|
||||
m_pathOrigin = m_path->origin;
|
||||
|
||||
return m_currentNodeIndex; // to satisfy static-code analyzers
|
||||
}
|
||||
|
||||
|
|
@ -2004,7 +2085,7 @@ int Bot::findDefendNode (const Vector &origin) {
|
|||
// provides enough cover point, and is far away from the defending position
|
||||
|
||||
if (m_currentNodeIndex == kInvalidNodeIndex) {
|
||||
m_currentNodeIndex = changePointIndex (findNearestNode ());
|
||||
changeNodeIndex (findNearestNode ());
|
||||
}
|
||||
TraceResult tr {};
|
||||
|
||||
|
|
@ -2020,7 +2101,7 @@ int Bot::findDefendNode (const Vector &origin) {
|
|||
int srcIndex = m_currentNodeIndex;
|
||||
|
||||
// max search distance
|
||||
const int kMaxDistance = 128 * bots.getBotCount ();
|
||||
const int kMaxDistance = cr::clamp (148 * bots.getBotCount (), 256, 1024);
|
||||
|
||||
// some of points not found, return random one
|
||||
if (srcIndex == kInvalidNodeIndex || posIndex == kInvalidNodeIndex) {
|
||||
|
|
@ -2177,7 +2258,7 @@ int Bot::findCoverNode (float maxDistance) {
|
|||
}
|
||||
|
||||
// ensure we're on valid point
|
||||
changePointIndex (srcIndex);
|
||||
changeNodeIndex (srcIndex);
|
||||
|
||||
// find the best node now
|
||||
for (int i = 0; i < graph.length (); ++i) {
|
||||
|
|
@ -2207,35 +2288,35 @@ int Bot::findCoverNode (float maxDistance) {
|
|||
continue;
|
||||
}
|
||||
|
||||
if (distance > minDistance[0]) {
|
||||
if (distance < minDistance[0]) {
|
||||
nodeIndex[0] = i;
|
||||
minDistance[0] = distance;
|
||||
}
|
||||
else if (distance > minDistance[1]) {
|
||||
else if (distance < minDistance[1]) {
|
||||
nodeIndex[1] = i;
|
||||
minDistance[1] = distance;
|
||||
}
|
||||
else if (distance > minDistance[2]) {
|
||||
else if (distance < minDistance[2]) {
|
||||
nodeIndex[2] = i;
|
||||
minDistance[2] = distance;
|
||||
}
|
||||
else if (distance > minDistance[3]) {
|
||||
else if (distance < minDistance[3]) {
|
||||
nodeIndex[3] = i;
|
||||
minDistance[3] = distance;
|
||||
}
|
||||
else if (distance > minDistance[4]) {
|
||||
else if (distance < minDistance[4]) {
|
||||
nodeIndex[4] = i;
|
||||
minDistance[4] = distance;
|
||||
}
|
||||
else if (distance > minDistance[5]) {
|
||||
else if (distance < minDistance[5]) {
|
||||
nodeIndex[5] = i;
|
||||
minDistance[5] = distance;
|
||||
}
|
||||
else if (distance > minDistance[6]) {
|
||||
else if (distance < minDistance[6]) {
|
||||
nodeIndex[6] = i;
|
||||
minDistance[6] = distance;
|
||||
}
|
||||
else if (distance > minDistance[7]) {
|
||||
else if (distance < minDistance[7]) {
|
||||
nodeIndex[7] = i;
|
||||
minDistance[7] = distance;
|
||||
}
|
||||
|
|
@ -2411,7 +2492,7 @@ bool Bot::advanceMovement () {
|
|||
}
|
||||
// check if bot is going to jump
|
||||
bool willJump = false;
|
||||
float jumpDistance = 0.0f;
|
||||
m_jumpDistance = 0.0f;
|
||||
|
||||
Vector src;
|
||||
Vector dst;
|
||||
|
|
@ -2420,15 +2501,15 @@ bool Bot::advanceMovement () {
|
|||
if (m_pathWalk.hasNext ()) {
|
||||
auto nextIndex = m_pathWalk.next ();
|
||||
|
||||
const Path &path = graph[destIndex];
|
||||
const Path &next = graph[nextIndex];
|
||||
const auto &path = graph[destIndex];
|
||||
const auto &next = graph[nextIndex];
|
||||
|
||||
for (const auto &link : path.links) {
|
||||
if (link.index == nextIndex && (link.flags & PathFlag::Jump)) {
|
||||
src = path.origin;
|
||||
dst = next.origin;
|
||||
|
||||
jumpDistance = path.origin.distance (next.origin);
|
||||
m_jumpDistance = path.origin.distance (next.origin);
|
||||
willJump = true;
|
||||
|
||||
break;
|
||||
|
|
@ -2437,14 +2518,14 @@ bool Bot::advanceMovement () {
|
|||
}
|
||||
|
||||
// is there a jump node right ahead and do we need to draw out the light weapon ?
|
||||
if (willJump && !usesKnife () && m_currentWeapon != Weapon::Scout && !m_isReloading && !usesPistol () && (jumpDistance > 200.0f || (dst.z - 32.0f > src.z && jumpDistance > 150.0f)) && !(m_states & (Sense::SeeingEnemy | Sense::SuspectEnemy))) {
|
||||
selectWeaponByName ("weapon_knife"); // draw out the knife if we needed
|
||||
if (willJump && !usesKnife () && m_currentWeapon != Weapon::Scout && !m_isReloading && !usesPistol () && (m_jumpDistance > 175.0f || (dst.z - 32.0f > src.z && m_jumpDistance > 135.0f)) && !(m_states & Sense::SeeingEnemy)) {
|
||||
selectWeaponById (Weapon::Knife); // draw out the knife if we needed
|
||||
}
|
||||
|
||||
// bot not already on ladder but will be soon?
|
||||
if ((graph[destIndex].flags & NodeFlag::Ladder) && !isOnLadder ()) {
|
||||
// get ladder nodes used by other (first moving) bots
|
||||
|
||||
// get ladder nodes used by other (first moving) bots
|
||||
for (const auto &other : bots) {
|
||||
// if another bot uses this ladder, wait 3 secs
|
||||
if (other.get () != this && other->m_notKilled && other->m_currentNodeIndex == destIndex) {
|
||||
|
|
@ -2454,10 +2535,9 @@ bool Bot::advanceMovement () {
|
|||
}
|
||||
}
|
||||
}
|
||||
changePointIndex (destIndex);
|
||||
changeNodeIndex (destIndex);
|
||||
}
|
||||
}
|
||||
m_pathOrigin = m_path->origin;
|
||||
|
||||
// if wayzone radius non zero vary origin a bit depending on the body angles
|
||||
if (m_path->radius > 0.0f) {
|
||||
|
|
@ -2484,7 +2564,7 @@ bool Bot::cantMoveForward (const Vector &normal, TraceResult *tr) {
|
|||
// first do a trace from the bot's eyes forward...
|
||||
auto src = getEyesPos ();
|
||||
auto forward = src + normal * 24.0f;
|
||||
auto right = Vector (0.0f, pev->angles.y, 0.0f).right ();
|
||||
const auto &right = Vector (0.0f, pev->angles.y, 0.0f).right ();
|
||||
|
||||
bool traceResult = false;
|
||||
|
||||
|
|
@ -2531,7 +2611,7 @@ bool Bot::cantMoveForward (const Vector &normal, TraceResult *tr) {
|
|||
}
|
||||
|
||||
// now check below waist
|
||||
if (pev->flags & FL_DUCKING) {
|
||||
if (isDucking ()) {
|
||||
src = pev->origin + Vector (0.0f, 0.0f, -19.0f + 19.0f);
|
||||
forward = src + Vector (0.0f, 0.0f, 10.0f) + normal * 24.0f;
|
||||
|
||||
|
|
@ -2646,7 +2726,7 @@ bool Bot::canJumpUp (const Vector &normal) {
|
|||
if (!isOnFloor () && (isOnLadder () || !isInWater ())) {
|
||||
return false;
|
||||
}
|
||||
auto right = Vector (0.0f, pev->angles.y, 0.0f).right (); // convert current view angle to vectors for traceline math...
|
||||
const auto &right = Vector (0.0f, pev->angles.y, 0.0f).right (); // convert current view angle to vectors for traceline math...
|
||||
|
||||
// check for normal jump height first...
|
||||
auto src = pev->origin + Vector (0.0f, 0.0f, -36.0f + 45.0f);
|
||||
|
|
@ -2793,7 +2873,7 @@ bool Bot::canDuckUnder (const Vector &normal) {
|
|||
Vector baseHeight;
|
||||
|
||||
// use center of the body first...
|
||||
if (pev->flags & FL_DUCKING) {
|
||||
if (isDucking ()) {
|
||||
baseHeight = pev->origin + Vector (0.0f, 0.0f, -17.0f);
|
||||
}
|
||||
else {
|
||||
|
|
@ -2812,7 +2892,7 @@ bool Bot::canDuckUnder (const Vector &normal) {
|
|||
}
|
||||
|
||||
// convert current view angle to vectors for TraceLine math...
|
||||
auto right = Vector (0.0f, pev->angles.y, 0.0f).right ();
|
||||
const auto &right = Vector (0.0f, pev->angles.y, 0.0f).right ();
|
||||
|
||||
// now check same height to one side of the bot...
|
||||
src = baseHeight + right * 16.0f;
|
||||
|
|
@ -3008,16 +3088,17 @@ void Bot::changeYaw (float speed) {
|
|||
pev->angles.y = pev->v_angle.y;
|
||||
}
|
||||
|
||||
int Bot::findCampingDirection () {
|
||||
int Bot::getRandomCampDir () {
|
||||
// find a good node to look at when camping
|
||||
|
||||
if (m_currentNodeIndex == kInvalidNodeIndex) {
|
||||
m_currentNodeIndex = changePointIndex (findNearestNode ());
|
||||
changeNodeIndex (findNearestNode ());
|
||||
}
|
||||
constexpr int kMaxNodesToSearch = 5;
|
||||
|
||||
int count = 0, indices[3] {};
|
||||
float distTab[3] {};
|
||||
uint16 visibility[3] {};
|
||||
int count = 0, indices[kMaxNodesToSearch] {};
|
||||
float distTab[kMaxNodesToSearch] {};
|
||||
uint16_t visibility[kMaxNodesToSearch] {};
|
||||
|
||||
int currentNode = m_currentNodeIndex;
|
||||
|
||||
|
|
@ -3025,9 +3106,9 @@ int Bot::findCampingDirection () {
|
|||
if (currentNode == i || !graph.isVisible (currentNode, i)) {
|
||||
continue;
|
||||
}
|
||||
const Path &path = graph[i];
|
||||
const auto &path = graph[i];
|
||||
|
||||
if (count < 3) {
|
||||
if (count < kMaxNodesToSearch) {
|
||||
indices[count] = i;
|
||||
|
||||
distTab[count] = pev->origin.distanceSq (path.origin);
|
||||
|
|
@ -3037,9 +3118,9 @@ int Bot::findCampingDirection () {
|
|||
}
|
||||
else {
|
||||
float distance = pev->origin.distanceSq (path.origin);
|
||||
uint16 visBits = path.vis.crouch + path.vis.stand;
|
||||
uint16_t visBits = path.vis.crouch + path.vis.stand;
|
||||
|
||||
for (int j = 0; j < 3; ++j) {
|
||||
for (int j = 0; j < kMaxNodesToSearch; ++j) {
|
||||
if (visBits >= visibility[j] && distance > distTab[j]) {
|
||||
indices[j] = i;
|
||||
|
||||
|
|
@ -3077,7 +3158,7 @@ void Bot::updateLookAngles () {
|
|||
|
||||
// adjust all body and view angles to face an absolute vector
|
||||
Vector direction = (m_lookAt - getEyesPos ()).angles ();
|
||||
direction.x *= -1.0f; // invert for engine
|
||||
direction.x = -direction.x; // invert for engine
|
||||
|
||||
direction.clampAngles ();
|
||||
|
||||
|
|
@ -3089,18 +3170,14 @@ void Bot::updateLookAngles () {
|
|||
return;
|
||||
}
|
||||
|
||||
if (m_difficulty == Difficulty::Expert && (m_aimFlags & AimFlags::Enemy) && (m_wantsToFire || usesSniper ()) && m_kpdRatio < 1.0f && m_healthValue < 50.0f) {
|
||||
pev->v_angle = direction;
|
||||
updateBodyAngles ();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
float accelerate = 3000.0f;
|
||||
float stiffness = 200.0f;
|
||||
float damping = 25.0f;
|
||||
|
||||
if ((m_aimFlags & (AimFlags::Enemy | AimFlags::Entity | AimFlags::Grenade)) && m_difficulty > Difficulty::Hard) {
|
||||
if (((m_aimFlags & (AimFlags::Enemy | AimFlags::Entity | AimFlags::Grenade)) || m_wantsToFire) && m_difficulty > Difficulty::Normal) {
|
||||
if (m_difficulty == Difficulty::Expert) {
|
||||
accelerate += 600.0f;
|
||||
}
|
||||
stiffness += 100.0f;
|
||||
damping += 5.0f;
|
||||
}
|
||||
|
|
@ -3275,14 +3352,10 @@ bool Bot::isOccupiedNode (int index, bool needZeroVelocity) {
|
|||
}
|
||||
auto bot = bots[client.ent];
|
||||
|
||||
if (bot == nullptr || bot == this || !bot->m_notKilled || bot->getTask ()->data == index) {
|
||||
if (bot == nullptr || bot == this || !bot->m_notKilled) {
|
||||
continue;
|
||||
}
|
||||
auto occupyId = util.getShootingCone (bot->ent (), pev->origin) >= 0.7f ? bot->m_previousNodes[0] : bot->m_currentNodeIndex;
|
||||
|
||||
if (index == occupyId) {
|
||||
return true;
|
||||
}
|
||||
return index == bot->m_currentNodeIndex || bot->getTask ()->data == index;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
@ -3366,10 +3439,17 @@ bool Bot::isReachableNode (int index) {
|
|||
}
|
||||
|
||||
bool Bot::isBannedNode (int index) {
|
||||
if (graph.exists (cv_debug_goal.int_ ())) {
|
||||
if (graph.exists (cv_debug_goal.int_ ()) || !graph.exists (index)) {
|
||||
return false;
|
||||
}
|
||||
for (const auto &node : m_goalHistory) {
|
||||
const auto &bucket = graph.getNodesInBucket (graph[index].origin);
|
||||
|
||||
// too few nodes in bucket near location, do not ban anything
|
||||
if (bucket.length <int> () <= kMaxNodeLinks) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const auto &node : m_nodeHistory) {
|
||||
if (node == index) {
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue