From 6e09cee0ee4761083ff3e9e5e64946f37f352f56 Mon Sep 17 00:00:00 2001 From: jeefo Date: Mon, 11 Mar 2024 15:26:11 +0300 Subject: [PATCH] fix: bots use buttons too extensively nav: added more cpu-friendly version to check if bot will fall in combat --- inc/graph.h | 1 + inc/yapb.h | 1 + src/botlib.cpp | 7 +---- src/combat.cpp | 4 +-- src/navigate.cpp | 70 +++++++++++++++++++++++++++++++----------------- src/tasks.cpp | 8 +++--- 6 files changed, 54 insertions(+), 37 deletions(-) diff --git a/inc/graph.h b/inc/graph.h index b529cb1..21e03c2 100644 --- a/inc/graph.h +++ b/inc/graph.h @@ -12,6 +12,7 @@ constexpr int kMaxNodeLinks = 8; // max links for single node // defines for nodes flags field (32 bits are available) CR_DECLARE_SCOPED_ENUM (NodeFlag, + Button = cr::bit (0), // use a nearby button (lifts, doors, etc.) Lift = cr::bit (1), // wait for lift to be down before approaching this node Crouch = cr::bit (2), // must crouch to reach this node Crossing = cr::bit (3), // a target node diff --git a/inc/yapb.h b/inc/yapb.h index 01ce7e7..d66df05 100644 --- a/inc/yapb.h +++ b/inc/yapb.h @@ -403,6 +403,7 @@ private: bool isKnifeMode (); bool isGrenadeWar (); bool isDeadlyMove (const Vector &to); + bool isSafeToMove (const Vector &to); bool isOutOfBombTimer (); bool isWeaponBadAtDistance (int weaponIndex, float distance); bool needToPauseFiring (float distance); diff --git a/src/botlib.cpp b/src/botlib.cpp index 233a078..07559bd 100644 --- a/src/botlib.cpp +++ b/src/botlib.cpp @@ -1962,12 +1962,7 @@ void Bot::filterTasks () { filter[Task::PickupItem].desire = 50.0f; // always pickup button } else { - float distance = (500.0f - pev->origin.distance (game.getEntityOrigin (m_pickupItem))) * 0.2f; - - if (distance > 50.0f) { - distance = 50.0f; - } - filter[Task::PickupItem].desire = distance; + filter[Task::PickupItem].desire = cr::max (50.0f, 500.0f - pev->origin.distance (game.getEntityOrigin (m_pickupItem)) * 0.2f); } } else { diff --git a/src/combat.cpp b/src/combat.cpp index a0a8b39..1b499d2 100644 --- a/src/combat.cpp +++ b/src/combat.cpp @@ -1409,7 +1409,7 @@ void Bot::attackMovement () { m_duckTime = game.time () - 1.0f; } - if (!isInWater () && !isOnLadder () && (m_moveSpeed > 0.0f || m_strafeSpeed >= 0.0f)) { + if (!isInWater () && !isOnLadder () && (m_moveSpeed > 0.0f || m_strafeSpeed > 0.0f)) { Vector right, forward; pev->v_angle.angleVectors (&forward, &right, nullptr); @@ -1417,7 +1417,7 @@ void Bot::attackMovement () { const auto &side = right * m_strafeSpeed * 0.2f; const auto &spot = pev->origin + front + side + pev->velocity * m_frameInterval; - if (isDeadlyMove (spot)) { + if (isSafeToMove (spot)) { m_strafeSpeed = -m_strafeSpeed; m_moveSpeed = -m_moveSpeed; diff --git a/src/navigate.cpp b/src/navigate.cpp index a9ecd69..1438395 100644 --- a/src/navigate.cpp +++ b/src/navigate.cpp @@ -1014,7 +1014,7 @@ bool Bot::updateNavigation () { TraceResult tr {}; // check if we are going through a door... - if (game.mapIs (MapFlags::HasDoors)) { + if (game.mapIs (MapFlags::HasDoors) || (m_pathFlags & NodeFlag::Button)) { game.testLine (pev->origin, m_pathOrigin, TraceIgnore::Monsters, ent (), &tr); if (!game.isNullEntity (tr.pHit) && game.isNullEntity (m_liftEntity) && util.isDoorEntity (tr.pHit)) { @@ -1022,14 +1022,16 @@ bool Bot::updateNavigation () { if (pev->origin.distanceSq (game.getEntityOrigin (tr.pHit)) < cr::sqrf (50.0f)) { ignoreCollision (); // don't consider being stuck + // also 'use' the door randomly if (rg.chance (50)) { // do not use door directly under xash, or we will get failed assert in gamedll code if (game.is (GameFlags::Xash3D)) { pev->button |= IN_USE; } else { - MDLL_Use (tr.pHit, ent ()); // also 'use' the door randomly + MDLL_Use (tr.pHit, ent ()); } + m_buttonPushTime = game.time () + 1.5f; } } @@ -1037,18 +1039,21 @@ bool Bot::updateNavigation () { m_aimFlags &= ~(AimFlags::LastEnemy | AimFlags::PredictPath); m_canChooseAimDirection = false; - auto button = lookupButton (tr.pHit->v.targetname.chars ()); + // delay task + if (m_buttonPushTime < game.time ()) { + auto button = lookupButton (tr.pHit->v.targetname.chars ()); - // check if we got valid button - if (!game.isNullEntity (button)) { - m_pickupItem = button; - m_pickupType = Pickup::Button; + // check if we got valid button + if (!game.isNullEntity (button)) { + m_pickupItem = button; + m_pickupType = Pickup::Button; - m_navTimeset = game.time (); + m_navTimeset = game.time (); + } } // if bot hits the door, then it opens, so wait a bit to let it open safely - if (pev->velocity.lengthSq2d () < cr::sqrf (10.0f) && m_timeDoorOpen < game.time ()) { + if (pev->velocity.lengthSq2d () < cr::sqrf (2.0f) && m_timeDoorOpen < game.time ()) { startTask (Task::Pause, TaskPri::Pause, kInvalidNodeIndex, game.time () + 0.5f, false); m_timeDoorOpen = game.time () + 1.0f; // retry in 1 sec until door is open @@ -2865,40 +2870,39 @@ bool Bot::isDeadlyMove (const Vector &to) { // this function eturns if given location would hurt Bot with falling damage TraceResult tr {}; - const auto &direction = (to - pev->origin).normalize_apx (); // 1 unit long + + constexpr auto kUnitsDown = 1000.0f; + constexpr auto kFallLimit = 150.0f; Vector check = to, down = to; - down.z -= 1000.0f; // straight down 1000 units + down.z -= kUnitsDown; // straight down 1000 units - game.testHull (check, down, TraceIgnore::Monsters, head_hull, ent (), &tr); + game.testLine (check, down, TraceIgnore::Monsters, ent (), &tr); - if (tr.flFraction > 0.036f) { // we're not on ground anymore? - tr.flFraction = 0.036f; - } - - float lastHeight = tr.flFraction * 1000.0f; // height from ground + float lastHeight = tr.flFraction * kUnitsDown; // height from ground float distanceSq = to.distanceSq (check); // distance from goal - if (distanceSq <= cr::sqrf (30.0f) && lastHeight > 150.0f) { + if (distanceSq <= cr::sqrf (16.0f) && lastHeight > kFallLimit) { return true; } + const auto &moveDir = (to - pev->origin).normalize_apx (); // 1 unit long - while (distanceSq > cr::sqrf (30.0f)) { - check = check - direction * 30.0f; // move 10 units closer to the goal... + while (distanceSq > cr::sqrf (16.0f)) { + check = check - moveDir * 16.0f; // move 16 units closer to the goal... down = check; - down.z -= 1000.0f; // straight down 1000 units + down.z -= kUnitsDown; // straight down 1000 units - game.testHull (check, down, TraceIgnore::Monsters, head_hull, ent (), &tr); + game.testLine (check, down, TraceIgnore::Monsters, ent (), &tr); // wall blocking? if (tr.fStartSolid) { return false; } - const float height = tr.flFraction * 1000.0f; // height from ground + const auto height = tr.flFraction * 1000.0f; // height from ground // drops more than 150 units? - if (lastHeight < height - 150.0f) { + if (lastHeight < height - kFallLimit) { return true; } lastHeight = height; @@ -2907,6 +2911,18 @@ bool Bot::isDeadlyMove (const Vector &to) { return false; } +bool Bot::isSafeToMove (const Vector &to) { + // simplified version of isDeadlyMove() just for combat movement checking + + constexpr auto kUnitsDown = 1000.0f; + constexpr auto kFallLimit = 150.0f; + + TraceResult tr {}; + game.testLine (to, to + Vector { 0.0f, 0.0f, -kUnitsDown }, TraceIgnore::Monsters, ent (), &tr); + + return tr.fStartSolid || tr.flFraction * kUnitsDown > kFallLimit; +} + int Bot::getRandomCampDir () { // find a good node to look at when camping @@ -3033,12 +3049,16 @@ edict_t *Bot::lookupButton (StringRef target) { float nearestDistanceSq = kInfiniteDistance; edict_t *result = nullptr; + TraceResult tr {}; + // find the nearest button which can open our target game.searchEntities ("target", target, [&] (edict_t *ent) { const Vector &pos = game.getEntityOrigin (ent); + game.testLine (pev->origin, pos, TraceIgnore::Monsters, pev->pContainingEntity, &tr); + // check if this place safe - if (!isDeadlyMove (pos)) { + if (tr.pHit == ent && tr.flFraction > 0.95f && !isDeadlyMove (pos)) { const float distanceSq = pev->origin.distanceSq (pos); // check if we got more close button diff --git a/src/tasks.cpp b/src/tasks.cpp index 86544b0..cabdaa0 100644 --- a/src/tasks.cpp +++ b/src/tasks.cpp @@ -1676,16 +1676,13 @@ void Bot::pickupItem_ () { case Pickup::Button: m_aimFlags |= AimFlags::Entity; - if (game.isNullEntity (m_pickupItem) || m_buttonPushTime < game.time ()) { + if (game.isNullEntity (m_pickupItem)) { completeTask (); m_pickupType = Pickup::None; break; } - // find angles from bot origin to entity... - const float angleToEntity = isInFOV (dest - getEyesPos ()); - // near to the button? if (itemDistanceSq < cr::sqrf (90.0f)) { m_moveSpeed = 0.0f; @@ -1693,6 +1690,9 @@ void Bot::pickupItem_ () { m_moveToGoal = false; m_checkTerrain = false; + // find angles from bot origin to entity... + const float angleToEntity = isInFOV (dest - getEyesPos ()); + // facing it directly? if (angleToEntity <= 10.0f) { MDLL_Use (m_pickupItem, ent ());