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)
This commit is contained in:
commandcobra7 2024-06-06 15:31:07 +03:00 committed by jeefo
commit ece83734a1
6 changed files with 113 additions and 85 deletions

View file

@ -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.
// ---

View file

@ -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:

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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 <int> (result.length ()) > maxCount) {
break;

View file

@ -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 <int> (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
}
}