fix: breakable problems on some maps

nav: do not consider busy nodes as long-radii
nav: more fixes to player avoidance
This commit is contained in:
jeefo 2025-02-01 16:17:11 +03:00
commit 2a6ca1d914
No known key found for this signature in database
GPG key ID: D696786B81B667C8
3 changed files with 51 additions and 21 deletions

View file

@ -268,7 +268,7 @@ edict_t *Bot::lookupBreakable () {
// check if this isn't a triggered (bomb) breakable and if it takes damage. if true, shoot the crap! // check if this isn't a triggered (bomb) breakable and if it takes damage. if true, shoot the crap!
if (util.isBreakableEntity (hit)) { if (util.isBreakableEntity (hit)) {
m_breakableOrigin = tr.vecEndPos; m_breakableOrigin = game.getEntityOrigin (hit);
m_breakableEntity = hit; m_breakableEntity = hit;
return hit; return hit;
@ -276,14 +276,26 @@ edict_t *Bot::lookupBreakable () {
} }
return nullptr; return nullptr;
}; };
auto isGoodForUs = [&] (edict_t *ent) -> bool {
if (game.isNullEntity (ent)) {
return false;
}
for (const auto &br : m_ignoredBreakable) {
if (br == ent) {
return false;
}
}
return true;
};
auto hit = doLookup (pev->origin, m_destOrigin, detectBreakableDistance); auto hit = doLookup (pev->origin, m_destOrigin, detectBreakableDistance);
if (!game.isNullEntity (hit)) { if (isGoodForUs (hit)) {
return hit; return hit;
} }
hit = doLookup (getEyesPos (), m_destOrigin, detectBreakableDistance); hit = doLookup (getEyesPos (), m_destOrigin, detectBreakableDistance);
if (!game.isNullEntity (hit)) { if (isGoodForUs (hit)) {
return hit; return hit;
} }
m_breakableEntity = nullptr; m_breakableEntity = nullptr;
@ -4146,7 +4158,7 @@ void Bot::enteredBuyZone (int buyState) {
// if bot is in buy zone, try to buy ammo for this weapon... // if bot is in buy zone, try to buy ammo for this weapon...
if (m_seeEnemyTime + 12.0f < game.time () if (m_seeEnemyTime + 12.0f < game.time ()
&& m_lastEquipTime + 15.0f < game.time () && m_lastEquipTime + 30.0f < game.time ()
&& m_inBuyZone && m_inBuyZone
&& (bots.getRoundStartTime () + rg (10.0f, 20.0f) + mp_buytime.as <float> () < game.time ()) && (bots.getRoundStartTime () + rg (10.0f, 20.0f) + mp_buytime.as <float> () < game.time ())
&& !bots.isBombPlanted () && !bots.isBombPlanted ()

View file

@ -474,19 +474,26 @@ void Bot::doPlayerAvoidance (const Vector &normal) {
return; // no player avoiding when with semiclip plugin return; // no player avoiding when with semiclip plugin
} }
m_hindrance = nullptr; m_hindrance = nullptr;
float distanceSq = cr::sqrf (512.0f); float distanceSq = cr::sqrf (pev->maxspeed);
const auto ownPrio = bots.getPlayerPriority (ent ()); const auto ownPrio = bots.getPlayerPriority (ent ());
auto clearCamp = [] (edict_t *ent) { auto clearCamp = [&] (edict_t *ent) {
auto bot = bots[ent]; auto bot = bots[ent];
if (bot) { if (bot) {
auto tid = bot->getCurrentTaskId (); const auto tid = bot->getCurrentTaskId ();
const auto tid2 = getCurrentTaskId ();
if (tid == Task::Camp || tid == Task::Hide || tid == Task::Pause) { if ((m_currentNodeIndex == bot->m_currentNodeIndex)
&& (tid == Task::Camp || tid == Task::Hide || tid == Task::Pause)) {
bot->completeTask (); bot->completeTask ();
} }
if ((m_currentNodeIndex == bot->m_currentNodeIndex)
&& tid2 == Task::Camp || tid2 == Task::Hide || tid2 == Task::Pause) {
completeTask ();
findValidNode ();
}
} }
}; };
@ -499,6 +506,7 @@ void Bot::doPlayerAvoidance (const Vector &normal) {
// give some priorities to bot avoidance // give some priorities to bot avoidance
if (ownPrio < otherPrio) { if (ownPrio < otherPrio) {
clearCamp (client.ent);
continue; continue;
} }
@ -508,6 +516,7 @@ void Bot::doPlayerAvoidance (const Vector &normal) {
// ignore because we're already avoiding someone better // ignore because we're already avoiding someone better
if (avoidPrio < otherPrio) { if (avoidPrio < otherPrio) {
clearCamp (m_hindrance);
continue; continue;
} }
} }
@ -551,7 +560,10 @@ void Bot::doPlayerAvoidance (const Vector &normal) {
const auto &dir = (pev->origin - m_hindrance->v.origin).normalize2d_apx (); const auto &dir = (pev->origin - m_hindrance->v.origin).normalize2d_apx ();
// to start strafing, we have to first figure out if the target is on the left side or right side // to start strafing, we have to first figure out if the target is on the left side or right side
if (m_avoidAction == Dodge::None && m_path->radius > 16.0f && !isInNarrowPlace ()) { if ((m_avoidAction == Dodge::None
&& m_path->radius > 16.0f
&& !isInNarrowPlace ())
|| m_moveSpeed < 0.0f) {
if ((dir | right.normalize2d_apx ()) > 0.0f) { if ((dir | right.normalize2d_apx ()) > 0.0f) {
m_avoidAction = Dodge::Left; m_avoidAction = Dodge::Left;
@ -565,16 +577,12 @@ void Bot::doPlayerAvoidance (const Vector &normal) {
setStrafeSpeed (normal, -pev->maxspeed); setStrafeSpeed (normal, -pev->maxspeed);
} }
} }
m_isStuck = false;
m_navTimeset = game.time (); m_navTimeset = game.time ();
if (distanceSq < cr::sqrf (96.0f)) { if (distanceSq < cr::sqrf (96.0f)) {
if ((dir | forward.normalize2d_apx ()) < 0.0f) { if ((dir | forward.normalize2d_apx ()) < 0.0f) {
m_moveSpeed = -pev->maxspeed; m_moveSpeed = -pev->maxspeed;
} }
else {
m_moveSpeed = pev->maxspeed;
}
} }
ignoreCollision (); ignoreCollision ();
} }
@ -1316,12 +1324,6 @@ bool Bot::updateNavigation () {
desiredDistanceSq = 0.0f; desiredDistanceSq = 0.0f;
} }
// always increase reachability distance for occupied nodes
if (!(graph[m_path->number].flags & NodeFlag::Crouch) && isOccupiedNode (m_path->number, pathHasFlags)) {
desiredDistanceSq = cr::sqrf (96.0f);
nodeDistanceSq *= 0.5f;
}
// needs precise placement - check if we get past the point // needs precise placement - check if we get past the point
if (desiredDistanceSq < cr::sqrf (20.0f) && nodeDistanceSq < cr::sqrf (30.0f)) { if (desiredDistanceSq < cr::sqrf (20.0f) && nodeDistanceSq < cr::sqrf (30.0f)) {
const auto predictRangeSq = m_pathOrigin.distanceSq (pev->origin + pev->velocity * m_frameInterval); const auto predictRangeSq = m_pathOrigin.distanceSq (pev->origin + pev->velocity * m_frameInterval);
@ -1344,7 +1346,7 @@ bool Bot::updateNavigation () {
return true; return true;
} }
if (nodeDistanceSq <= desiredDistanceSq) { if (nodeDistanceSq < desiredDistanceSq) {
// did we reach a destination node? // did we reach a destination node?
if (getTask ()->data == m_currentNodeIndex) { if (getTask ()->data == m_currentNodeIndex) {
if (m_chosenGoalIndex != kInvalidNodeIndex) { if (m_chosenGoalIndex != kInvalidNodeIndex) {
@ -1816,7 +1818,9 @@ bool Bot::findNextBestNodeEx (const IntArray &data, bool handleFails) {
lessDist[i] = kInfiniteDistance; lessDist[i] = kInfiniteDistance;
lessIndex[i] = kInvalidNodeIndex; lessIndex[i] = kInvalidNodeIndex;
} }
const auto &numToSkip = handleFails ? 1 : rg (1, 4);
// in case low node density do not skip previous ones, in case of fail reduce max nodes to skip
const auto &numToSkip = graph.length () < 512 ? 0 : (handleFails ? 1 : rg (1, 4));
for (const auto &i : data) { for (const auto &i : data) {
const auto &path = graph[i]; const auto &path = graph[i];

View file

@ -1467,6 +1467,20 @@ void Bot::shootBreakable_ () {
completeTask (); completeTask ();
return; return;
} }
else {
TraceResult tr {};
game.testLine (pev->origin, m_breakableOrigin, TraceIgnore::None, ent (), &tr);
if (tr.pHit != m_breakableEntity || !util.isVisible (tr.vecEndPos, ent ())) {
m_ignoredBreakable.push (tr.pHit);
m_breakableEntity = nullptr;
m_breakableOrigin = nullptr;
completeTask ();
return;
}
}
m_aimFlags |= AimFlags::Override; m_aimFlags |= AimFlags::Override;
pev->button |= m_campButtons; pev->button |= m_campButtons;