fix: bots use buttons too extensively

nav: added more cpu-friendly version to check if bot will fall in combat
This commit is contained in:
jeefo 2024-03-11 15:26:11 +03:00
commit 6e09cee0ee
No known key found for this signature in database
GPG key ID: 927BCA0779BEA8ED
6 changed files with 54 additions and 37 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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 ());