From ece83734a1dd15bb69479bd3b77b061d59ac7a54 Mon Sep 17 00:00:00 2001 From: commandcobra7 <91374215+commandcobra7@users.noreply.github.com> Date: Thu, 6 Jun 2024 15:31:07 +0300 Subject: [PATCH] nav: return to everything visible in finding the nearest node. (resolve previously solved problem #432) nav: checks whether the robot is blocked in the direction of movement. (excluded in hostages) --- cfg/addons/yapb/conf/yapb.cfg | 14 +++++ inc/graph.h | 4 +- src/botlib.cpp | 26 ++++---- src/combat.cpp | 32 +++++----- src/graph.cpp | 10 +-- src/navigate.cpp | 112 ++++++++++++++++++---------------- 6 files changed, 113 insertions(+), 85 deletions(-) diff --git a/cfg/addons/yapb/conf/yapb.cfg b/cfg/addons/yapb/conf/yapb.cfg index 7a8fb07..e4811d8 100644 --- a/cfg/addons/yapb/conf/yapb.cfg +++ b/cfg/addons/yapb/conf/yapb.cfg @@ -298,6 +298,13 @@ yb_stab_close_enemies "1" // yb_use_engine_pvs_check "0" +// +// Use hitbox-based enemy targeting, instead of offset based. Use with the yb_use_engine_pvs_check enabled to reduce CPU usage. +// --- +// Default: "0", Min: "0", Max: "1" +// +yb_use_hitbox_enemy_targeting "0" + // // Binds specified key for opening bots menu. // --- @@ -662,6 +669,13 @@ yb_rotate_stay_max "3600.0" // yb_has_team_semiclip "0" +// +// Determines the maximum slope height change between the current and next node to consider the current link as a jump link. Only for generated graphs. +// --- +// Default: "24.0", Min: "12.0", Max: "48.0" +// +yb_graph_slope_height "24.0" + // // Selects the heuristic function mode. For debug purposes only. // --- diff --git a/inc/graph.h b/inc/graph.h index 182afa4..28d5d49 100644 --- a/inc/graph.h +++ b/inc/graph.h @@ -205,7 +205,7 @@ public: int getFarest (const Vector &origin, const float maxRange = 32.0); int getForAnalyzer (const Vector &origin, const float maxRange); int getNearest (const Vector &origin, const float range = kInfiniteDistance, int flags = -1); - int getNearestNoBuckets (const Vector &origin, const float range = kInfiniteDistance, int flags = -1); + int getNearestNoBuckets (const Vector &origin, float nearestDistanceSq = kInfiniteDistance, int flags = -1); int getEditorNearest (const float maxRange = 50.0f); int clearConnections (int index); int getBspSize (); @@ -264,7 +264,7 @@ public: void syncCollectOnline (); void collectOnline (); - IntArray getNearestInRadius (float radius, const Vector &origin, int maxCount = -1); + IntArray getNearestInRadius (float radiusSq, const Vector &origin, int maxCount = -1); const IntArray &getNodesInBucket (const Vector &pos); public: diff --git a/src/botlib.cpp b/src/botlib.cpp index df1b58b..e70e2a8 100644 --- a/src/botlib.cpp +++ b/src/botlib.cpp @@ -1658,10 +1658,10 @@ void Bot::overrideConditions () { // special handling, if we have a knife in our hands if (isKnifeMode () && (util.isPlayer (m_enemy) || (cv_attack_monsters && util.isMonster (m_enemy)))) { - const float distance2d = pev->origin.distance2d (m_enemy->v.origin); + const float distanceSq2d = pev->origin.distanceSq2d (m_enemy->v.origin); // do nodes movement if enemy is not reachable with a knife - if (distance2d > 250.0f && (m_states & Sense::SeeingEnemy)) { + if (distanceSq2d > cr::sqrf (250.0f) && (m_states & Sense::SeeingEnemy)) { const int nearestToEnemyPoint = graph.getNearest (m_enemy->v.origin); if (nearestToEnemyPoint != kInvalidNodeIndex @@ -1669,18 +1669,18 @@ void Bot::overrideConditions () { && cr::abs (graph[nearestToEnemyPoint].origin.z - m_enemy->v.origin.z) < 16.0f) { if (tid != Task::MoveToPosition && !cr::fequal (getTask ()->desire, TaskPri::Hide)) { - startTask (Task::MoveToPosition, TaskPri::Hide, nearestToEnemyPoint, game.time () + distance2d / (m_moveSpeed * 2.0f), true); + startTask (Task::MoveToPosition, TaskPri::Hide, nearestToEnemyPoint, game.time () + distanceSq2d / cr::sqrf (m_moveSpeed) * 2.0f, true); } else { if (tid == Task::MoveToPosition && getTask ()->data != nearestToEnemyPoint) { clearTask (Task::MoveToPosition); - startTask (Task::MoveToPosition, TaskPri::Hide, nearestToEnemyPoint, game.time () + distance2d / (m_moveSpeed * 2.0f), true); + startTask (Task::MoveToPosition, TaskPri::Hide, nearestToEnemyPoint, game.time () + distanceSq2d / cr::sqrf (m_moveSpeed) * 2.0f, true); } } } } else { - if (distance2d <= 250.0f && (m_states & Sense::SeeingEnemy) && tid == Task::MoveToPosition) { + if (distanceSq2d <= cr::sqrf (250.0f) && (m_states & Sense::SeeingEnemy) && tid == Task::MoveToPosition) { clearTask (Task::MoveToPosition); // remove any move tasks } } @@ -1756,7 +1756,7 @@ void Bot::syncUpdatePredictedIndex () { auto result = planner.find (destIndex, currentNodeIndex, [&] (int index) { ++pathLength; - if (vistab.visible (currentNodeIndex, index) && botOrigin.distanceSq (graph[index].origin) > cr::sqrf (256.0f)) { + if (vistab.visible (currentNodeIndex, index) && botOrigin.distanceSq (graph[index].origin) < cr::sqrf (2048.0f)) { bestIndex = index; return false; } @@ -1786,7 +1786,7 @@ void Bot::refreshEnemyPredict () { if (game.isNullEntity (m_enemy) && !game.isNullEntity (m_lastEnemy) && !m_lastEnemyOrigin.empty ()) { const auto distanceToLastEnemySq = m_lastEnemyOrigin.distanceSq (pev->origin); - if (distanceToLastEnemySq > cr::sqrf (256.0f) && (distanceToLastEnemySq < cr::sqrf (2048.0f) || usesSniper ())) { + if (distanceToLastEnemySq < cr::sqrf (2048.0f)) { m_aimFlags |= AimFlags::PredictPath; } const bool denyLastEnemy = pev->velocity.lengthSq2d () > 0.0f @@ -1872,11 +1872,11 @@ void Bot::setConditions () { } } else { - auto currentTime = game.time (); + m_killsInterval = m_lastVictimTime - game.time (); + + if (m_killsInterval <= 5.0f) { + ++m_killsCount; - m_killsInterval = currentTime - m_lastVictimTime; - if (m_killsInterval <= 5) { - m_killsCount++; if (m_killsCount > 2) { pushChatterMessage (Chatter::OnARoll); } @@ -2037,7 +2037,7 @@ void Bot::filterTasks () { else if (m_isVIP || m_isReloading || (sniping && usesSniper ())) { ratio *= 3.0f; // triple the seek cover desire if bot is VIP or reloading } - else if (m_lastEnemyOrigin.distanceSq2d (pev->origin) < cr::sqrf (200.0f)) { + else if (m_lastEnemyOrigin.distanceSq2d (pev->origin) < cr::sqrf (256.0f)) { ratio *= 3.0f; } else if (game.is (GameFlags::CSDM)) { @@ -2277,7 +2277,7 @@ void Bot::completeTask () { } bool Bot::isEnemyThreat () { - if (game.isNullEntity (m_enemy) || getCurrentTaskId () == Task::SeekCover) { + if (game.isNullEntity (m_enemy) || (m_states & Sense::SuspectEnemy) || getCurrentTaskId () == Task::SeekCover) { return false; } diff --git a/src/combat.cpp b/src/combat.cpp index a4f93bb..cf15e9f 100644 --- a/src/combat.cpp +++ b/src/combat.cpp @@ -226,7 +226,7 @@ bool Bot::checkBodyPartsWithOffsets (edict_t *target) { bool Bot::checkBodyPartsWithHitboxes (edict_t *target) { const auto self = pev->pContainingEntity; - const auto refersh = m_frameInterval * 1.5f; + const auto refresh = m_frameInterval * 1.5f; TraceResult result {}; const auto &eyes = getEyesPos (); @@ -240,7 +240,7 @@ bool Bot::checkBodyPartsWithHitboxes (edict_t *target) { // get the stomach hitbox m_enemyParts = Visibility::None; - game.testLine (eyes, m_hitboxEnumerator->get (target, PlayerPart::Stomach, refersh), ignoreFlags, self, &result); + game.testLine (eyes, m_hitboxEnumerator->get (target, PlayerPart::Stomach, refresh), ignoreFlags, self, &result); if (hitsTarget ()) { m_enemyParts |= Visibility::Body; @@ -249,7 +249,7 @@ bool Bot::checkBodyPartsWithHitboxes (edict_t *target) { // get the stomach hitbox m_enemyParts = Visibility::None; - game.testLine (eyes, m_hitboxEnumerator->get (target, PlayerPart::Head, refersh), ignoreFlags, self, &result); + game.testLine (eyes, m_hitboxEnumerator->get (target, PlayerPart::Head, refresh), ignoreFlags, self, &result); if (hitsTarget ()) { m_enemyParts |= Visibility::Head; @@ -262,7 +262,7 @@ bool Bot::checkBodyPartsWithHitboxes (edict_t *target) { // get the left hitbox m_enemyParts = Visibility::None; - game.testLine (eyes, m_hitboxEnumerator->get (target, PlayerPart::LeftArm, refersh), ignoreFlags, self, &result); + game.testLine (eyes, m_hitboxEnumerator->get (target, PlayerPart::LeftArm, refresh), ignoreFlags, self, &result); if (hitsTarget ()) { m_enemyParts |= Visibility::Other; @@ -273,7 +273,7 @@ bool Bot::checkBodyPartsWithHitboxes (edict_t *target) { // get the right hitbox m_enemyParts = Visibility::None; - game.testLine (eyes, m_hitboxEnumerator->get (target, PlayerPart::RightArm, refersh), ignoreFlags, self, &result); + game.testLine (eyes, m_hitboxEnumerator->get (target, PlayerPart::RightArm, refresh), ignoreFlags, self, &result); if (hitsTarget ()) { m_enemyParts |= Visibility::Other; @@ -284,7 +284,7 @@ bool Bot::checkBodyPartsWithHitboxes (edict_t *target) { // get the feet spot m_enemyParts = Visibility::None; - game.testLine (eyes, m_hitboxEnumerator->get (target, PlayerPart::Feet, refersh), ignoreFlags, self, &result); + game.testLine (eyes, m_hitboxEnumerator->get (target, PlayerPart::Feet, refresh), ignoreFlags, self, &result); if (hitsTarget ()) { m_enemyParts |= Visibility::Other; @@ -1373,7 +1373,7 @@ void Bot::attackMovement () { } auto approach = 0; - const auto distance = m_lookAt.distance (getEyesPos ()); // how far away is the enemy scum? + const auto distanceSq = m_lookAt.distanceSq (getEyesPos ()); // how far away is the enemy scum? if (usesKnife ()) { approach = 100; @@ -1394,7 +1394,11 @@ void Bot::attackMovement () { // only take cover when bomb is not planted and enemy can see the bot or the bot is VIP if (!game.is (GameFlags::CSDM)) { - if ((m_states & Sense::SeeingEnemy) && approach < 30 && !bots.isBombPlanted () && (isInViewCone (m_enemy->v.origin) || m_isVIP)) { + if ((m_states & Sense::SeeingEnemy) + && approach < 30 + && !bots.isBombPlanted () + && (isInViewCone (m_enemy->v.origin) || m_isVIP)) { + if (m_retreatTime < game.time ()) { startTask (Task::SeekCover, TaskPri::SeekCover, kInvalidNodeIndex, 0.0f, true); } @@ -1419,10 +1423,10 @@ void Bot::attackMovement () { else if (usesRifle () || usesSubmachine () || usesHeavy ()) { const int rand = rg (1, 100); - if (distance < 768.0f) { + if (distanceSq < cr::sqrf (768.0f)) { m_fightStyle = Fight::Strafe; } - else if (distance < 1024.0f) { + else if (distanceSq < cr::sqrf (1024.0f)) { if (isGroupOfEnemies (m_enemy->v.origin)) { m_fightStyle = Fight::Strafe; } @@ -1460,14 +1464,14 @@ void Bot::attackMovement () { // fire hurts friend value here is from previous frame, but acceptable, and saves us alot of cpu cycles if (approach < 30 || m_fireHurtsFriend || ((usesPistol () || usesShotgun ()) - && distance < pistolStrafeDistance + && distanceSq < cr::sqrf (pistolStrafeDistance) && isInViewCone (m_enemyOrigin))) { m_fightStyle = Fight::Strafe; } m_lastFightStyleCheck = game.time () + 3.0f; } - if (distance < 96.0f && !usesKnife ()) { + if (distanceSq < cr::sqrf (96.0f) && !usesKnife ()) { m_moveSpeed = -pev->maxspeed; } @@ -1556,7 +1560,7 @@ void Bot::attackMovement () { if (alreadyDucking) { m_duckTime = game.time () + m_frameInterval * 3.0f; } - else if ((distance > kSprayDistanceX2 && hasPrimaryWeapon ()) + else if ((distanceSq > cr::sqrf (kSprayDistanceX2) && hasPrimaryWeapon ()) && isFullView && getCurrentTaskId () != Task::SeekCover && getCurrentTaskId () != Task::Hunt) { @@ -1573,7 +1577,7 @@ void Bot::attackMovement () { } if (m_difficulty >= Difficulty::Normal && isOnFloor () && m_duckTime < game.time ()) { - if (distance < kSprayDistanceX2) { + if (distanceSq < cr::sqrf (kSprayDistanceX2)) { if (rg (0, 1000) < rg (5, 10) && pev->velocity.length2d () > 150.0f && isInViewCone (m_enemy->v.origin)) { pev->button |= IN_JUMP; } diff --git a/src/graph.cpp b/src/graph.cpp index cee9ba1..bb40aad 100644 --- a/src/graph.cpp +++ b/src/graph.cpp @@ -493,12 +493,12 @@ int BotGraph::getForAnalyzer (const Vector &origin, const float maxRange) { return index; } -int BotGraph::getNearestNoBuckets (const Vector &origin, const float range, int flags) { +int BotGraph::getNearestNoBuckets (const Vector &origin, float nearestDistanceSq, int flags) { // find the nearest node to that origin and return the index // fallback and go thru wall the nodes... int index = kInvalidNodeIndex; - float nearestDistanceSq = cr::sqrf (range); + nearestDistanceSq = cr::sqrf (nearestDistanceSq); for (const auto &path : m_paths) { if (flags != -1 && !(path.flags & flags)) { @@ -555,15 +555,15 @@ int BotGraph::getNearest (const Vector &origin, const float range, int flags) { return index; } -IntArray BotGraph::getNearestInRadius (float radius, const Vector &origin, int maxCount) { +IntArray BotGraph::getNearestInRadius (float radiusSq, const Vector &origin, int maxCount) { // returns all nodes within radius from position - const float radiusSq = cr::sqrf (radius); + radiusSq = cr::sqrf (radiusSq); IntArray result {}; const auto &bucket = getNodesInBucket (origin); - if (bucket.length () < kMaxNodeLinks || radius > cr::sqrf (256.0f)) { + if (bucket.length () < kMaxNodeLinks || radiusSq > cr::sqrf (256.0f)) { for (const auto &path : m_paths) { if (maxCount != -1 && static_cast (result.length ()) > maxCount) { break; diff --git a/src/navigate.cpp b/src/navigate.cpp index ac3bdd6..d29657f 100644 --- a/src/navigate.cpp +++ b/src/navigate.cpp @@ -370,10 +370,6 @@ void Bot::postProcessGoals (const IntArray &goals, int result[]) { return true; } - if (!isOccupiedNode (index)) { - return true; - } - // check if historical goal for (const auto &hg : m_goalHist) { if (hg == index) { @@ -874,7 +870,7 @@ void Bot::checkFall () { fixFall = true; } else if (m_currentNodeIndex != kInvalidNodeIndex) { - if (pev->origin.distanceSq (m_checkFallPoint[1]) <= cr::sqrf (32.0f) && pev->origin.z + 16.0f < m_checkFallPoint[1].z) { + if (pev->origin.distanceSq (m_checkFallPoint[1]) <= cr::sqrf (32.0f) && pev->origin.z + 64.0f < m_checkFallPoint[1].z) { fixFall = true; } } @@ -1046,12 +1042,18 @@ bool Bot::updateNavigation () { } if (m_pathFlags & NodeFlag::Ladder) { + constexpr auto kLadderOffset = Vector (0.0f, 0.0f, 36.0f); + + const auto prevNodeIndex = m_previousNodes[0]; const float ladderDistance = pev->origin.distance (m_pathOrigin); - if (m_pathOrigin.z >= pev->origin.z + 16.0f) { - constexpr auto kLadderOffset = Vector (0.0f, 0.0f, 36.0f); - - m_pathOrigin = m_path->origin + kLadderOffset; + if (graph.exists (prevNodeIndex) && !(graph[prevNodeIndex].flags & NodeFlag::Ladder)) { + if (m_pathOrigin.z >= pev->origin.z + 16.0f) { + m_pathOrigin = m_path->origin + kLadderOffset; + } + else if (m_pathOrigin.z < pev->origin.z - 16.0f) { + m_pathOrigin = m_path->origin - kLadderOffset; + } } else if (m_pathOrigin.z < pev->origin.z + 16.0f && !isOnLadder () && isOnFloor () && !isDucking ()) { m_moveSpeed = ladderDistance; @@ -1063,7 +1065,6 @@ bool Bot::updateNavigation () { m_moveSpeed = pev->maxspeed; } } - const auto prevNodeIndex = m_previousNodes[0]; // do a precise movement when very near if (graph.exists (prevNodeIndex) && !(graph[prevNodeIndex].flags & NodeFlag::Ladder) && ladderDistance < 64.0f) { @@ -1082,39 +1083,32 @@ bool Bot::updateNavigation () { for (const auto &client : util.getClients ()) { if (!(client.flags & ClientFlags::Used) || !(client.flags & ClientFlags::Alive) - || (client.ent->v.movetype != MOVETYPE_FLY) || client.team != m_team || client.ent == ent ()) { continue; } - TraceResult tr {}; + + if (client.ent->v.origin.distanceSq (m_pathOrigin) > cr::sqrf (64.0f)) { + continue; + } bool foundGround = false; int previousNode = 0; - // more than likely someone is already using our ladder... - if (client.ent->v.origin.distanceSq (m_path->origin) < cr::sqrf (48.0f)) { - 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 (cr::abs (pev->origin.z - client.ent->v.origin.z) > 15.0f && (client.ent->v.movetype == MOVETYPE_FLY)) { + const auto numPreviousNode = rg (0, 2); - // 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)) { - - const auto numPreviousNode = rg (0, 2); - - for (int i = 0; i < numPreviousNode; ++i) { - if (graph.exists (m_previousNodes[i]) && (graph[m_previousNodes[i]].flags & NodeFlag::Ladder)) { - foundGround = true; - previousNode = m_previousNodes[i]; - break; - } - } - if (foundGround) { - changeNodeIndex (m_previousNodes[0]); - findPath (m_previousNodes[0], previousNode, m_pathType); + for (int i = 0; i < numPreviousNode; ++i) { + if (graph.exists (prevNodeIndex) && (graph[prevNodeIndex].flags & NodeFlag::Ladder)) { + foundGround = true; + previousNode = m_previousNodes[i]; + break; } } + if (foundGround) { + findPath (m_previousNodes[0], previousNode, m_pathType); + } } } } @@ -1252,11 +1246,13 @@ bool Bot::updateNavigation () { } // needs precise placement - check if we get past the point - if (desiredDistanceSq < cr::sqrf (22.0f) - && nodeDistanceSq < cr::sqrf (30.0f) - && m_pathOrigin.distanceSq (pev->origin + pev->velocity * m_frameInterval) >= nodeDistanceSq) { - - desiredDistanceSq = nodeDistanceSq + 1.0f; + if (desiredDistanceSq < cr::sqrf (22.0f) && nodeDistanceSq < cr::sqrf (30.0f)) { + if (m_pathOrigin.distanceSq (pev->origin + pev->velocity * m_frameInterval) >= nodeDistanceSq) { + desiredDistanceSq = nodeDistanceSq + 1.0f; + } + if (m_pathOrigin.distanceSq (pev->origin + pev->velocity * m_frameInterval) <= desiredDistanceSq) { + desiredDistanceSq = nodeDistanceSq + 1.0f; + } } // this allows us to prevent stupid bot behavior when he reaches almost end point of this route, but some one (other bot eg) @@ -1720,6 +1716,7 @@ bool Bot::findNextBestNode () { const auto &origin = pev->origin + pev->velocity * m_frameInterval; const auto &bucket = graph.getNodesInBucket (origin); + const auto &numToSkip = cr::clamp (rg (0, 2), 0, static_cast (bucket.length () / 2)); for (const auto &i : bucket) { const auto &path = graph[i]; @@ -1729,9 +1726,17 @@ bool Bot::findNextBestNode () { } bool skip = !!(path.number == m_currentNodeIndex); + // skip the current node, if any + if (skip && numToSkip > 0) { + continue; + } + // skip current or recent previous node - if (path.number == m_previousNodes[0]) { - skip = true; + for (int j = 0; j < numToSkip; ++j) { + if (path.number == m_previousNodes[j]) { + skip = true; + break; + } } // skip node from recent list @@ -2474,8 +2479,7 @@ bool Bot::advanceMovement () { bool willJump = false; float jumpDistanceSq = 0.0f; - Vector src {}; - Vector dst {}; + Vector src {}, dst {}; // try to find out about future connection flags if (m_pathWalk.hasNext ()) { @@ -2536,16 +2540,17 @@ bool Bot::advanceMovement () { void Bot::setPathOrigin () { constexpr int kMaxAlternatives = 5; + const float radius = m_path->radius; // if node radius non zero vary origin a bit depending on the body angles - if (m_path->radius > 0.0f) { + if (radius > 0.0f) { int nearestIndex = kInvalidNodeIndex; if (!m_pathWalk.empty () && m_pathWalk.hasNext ()) { Vector orgs[kMaxAlternatives] {}; for (int i = 0; i < kMaxAlternatives; ++i) { - orgs[i] = m_pathOrigin + Vector (rg (-m_path->radius, m_path->radius), rg (-m_path->radius, m_path->radius), 0.0f); + orgs[i] = m_pathOrigin + Vector (rg (-radius, radius), rg (-radius, radius), 0.0f); } float nearestDistanceSq = kInfiniteDistance; @@ -2565,7 +2570,7 @@ void Bot::setPathOrigin () { } if (nearestIndex == kInvalidNodeIndex) { - m_pathOrigin += Vector (pev->angles.x, cr::wrapAngle (pev->angles.y + rg (-90.0f, 90.0f)), 0.0f).forward () * rg (0.0f, m_path->radius); + m_pathOrigin += Vector (pev->angles.x, cr::wrapAngle (pev->angles.y + rg (-90.0f, 90.0f)), 0.0f).forward () * rg (0.0f, radius); } } @@ -2599,12 +2604,17 @@ bool Bot::isBlockedForward (const Vector &normal, TraceResult *tr) { return result->flFraction < 1.0f && !util.isDoorEntity (result->pHit); }; + auto checkHostage = [&] (TraceResult *result) { + return result->flFraction < 1.0f && m_team == Team::Terrorist && !util.isHostageEntity (result->pHit); + }; + // trace from the bot's eyes straight forward... game.testLine (src, forward, TraceIgnore::Monsters, ent (), tr); // check if the trace hit something... if (tr->flFraction < 1.0f) { - if (game.mapIs (MapFlags::HasDoors) && util.isDoorEntity (tr->pHit)) { + if ((game.mapIs (MapFlags::HasDoors) && util.isDoorEntity (tr->pHit)) + || (m_team == Team::CT && util.isHostageEntity (tr->pHit))) { return false; } return true; // bot's head will hit something @@ -2622,7 +2632,7 @@ bool Bot::isBlockedForward (const Vector &normal, TraceResult *tr) { game.testLine (src, forward, TraceIgnore::Monsters, ent (), tr); // check if the trace hit something... - if (checkDoor (tr)) { + if (checkDoor (tr) || checkHostage (tr)) { return true; // bot's body will hit something } @@ -2634,7 +2644,7 @@ bool Bot::isBlockedForward (const Vector &normal, TraceResult *tr) { game.testLine (src, forward, TraceIgnore::Monsters, ent (), tr); // check if the trace hit something... - if (checkDoor (tr)) { + if (checkDoor (tr) || checkHostage (tr)) { return true; // bot's body will hit something } @@ -2646,7 +2656,7 @@ bool Bot::isBlockedForward (const Vector &normal, TraceResult *tr) { game.testLine (src, forward, TraceIgnore::Monsters, ent (), tr); // check if the trace hit something... - if (checkDoor (tr)) { + if (checkDoor (tr) || checkHostage (tr)) { return true; // bot's body will hit something } src = pev->origin; @@ -2655,7 +2665,7 @@ bool Bot::isBlockedForward (const Vector &normal, TraceResult *tr) { game.testLine (src, forward, TraceIgnore::Monsters, ent (), tr); // check if the trace hit something... - if (checkDoor (tr)) { + if (checkDoor (tr) || checkHostage (tr)) { return true; // bot's body will hit something } } @@ -2671,7 +2681,7 @@ bool Bot::isBlockedForward (const Vector &normal, TraceResult *tr) { game.testLine (src, forward, TraceIgnore::Monsters, ent (), tr); // check if the trace hit something... - if (checkDoor (tr)) { + if (checkDoor (tr) || checkHostage (tr)) { return true; // bot's body will hit something } @@ -2682,7 +2692,7 @@ bool Bot::isBlockedForward (const Vector &normal, TraceResult *tr) { game.testLine (src, forward, TraceIgnore::Monsters, ent (), tr); // check if the trace hit something... - if (checkDoor (tr)) { + if (checkDoor (tr) || checkHostage (tr)) { return true; // bot's body will hit something } }