nav: various fixes to movement code

refactor: move some things into new game state class
This commit is contained in:
jeefo 2025-10-08 20:12:46 +03:00
commit 7b378ba3fa
No known key found for this signature in database
GPG key ID: D696786B81B667C8
29 changed files with 805 additions and 745 deletions

View file

@ -60,12 +60,12 @@ int Bot::findBestGoal () {
bool hasMoreHostagesAround = false;
// try to search nearby-unused hostage, and if so, go to next goal
if (bots.hasInterestingEntities ()) {
const auto &interesting = bots.getInterestingEntities ();
if (gameState.hasInterestingEntities ()) {
const auto &interesting = gameState.getInterestingEntities ();
// search world for hostages
for (const auto &ent : interesting) {
if (!util.isHostageEntity (ent)) {
if (!game.isHostageEntity (ent)) {
continue;
}
bool hostageInUse = false;
@ -134,7 +134,7 @@ int Bot::findBestGoal () {
}
}
else if (game.mapIs (MapFlags::Demolition) && m_team == Team::CT) {
if (bots.isBombPlanted () && getCurrentTaskId () != Task::EscapeFromBomb && !graph.getBombOrigin ().empty ()) {
if (gameState.isBombPlanted () && getCurrentTaskId () != Task::EscapeFromBomb && !gameState.getBombOrigin ().empty ()) {
if (bots.hasBombSay (BombPlantedSay::ChatSay)) {
pushChatMessage (Chat::Plant);
@ -149,10 +149,10 @@ int Bot::findBestGoal () {
defensive += 10.0f;
}
}
else if (game.mapIs (MapFlags::Demolition) && m_team == Team::Terrorist && bots.getRoundStartTime () + 10.0f < game.time ()) {
else if (game.mapIs (MapFlags::Demolition) && m_team == Team::Terrorist && gameState.getRoundStartTime () + 10.0f < game.time ()) {
// send some terrorists to guard planted bomb
if (!m_defendedBomb && bots.isBombPlanted () && getCurrentTaskId () != Task::EscapeFromBomb && getBombTimeleft () >= 15.0f) {
return m_chosenGoalIndex = findDefendNode (graph.getBombOrigin ());
if (!m_defendedBomb && gameState.isBombPlanted () && getCurrentTaskId () != Task::EscapeFromBomb && gameState.getBombTimeLeft () >= 15.0f) {
return m_chosenGoalIndex = findDefendNode (gameState.getBombOrigin ());
}
}
else if (game.mapIs (MapFlags::Escape)) {
@ -202,9 +202,9 @@ int Bot::findBestGoal () {
int Bot::findBestGoalWhenBombAction () {
int result = kInvalidNodeIndex;
if (!bots.isBombPlanted () && !cv_ignore_objectives) {
if (!gameState.isBombPlanted () && !cv_ignore_objectives) {
game.searchEntities ("classname", "weaponbox", [&] (edict_t *ent) {
if (util.isModel (ent, "backpack.mdl")) {
if (game.isEntityModelMatches (ent, "backpack.mdl")) {
result = graph.getNearest (game.getEntityOrigin (ent));
if (graph.exists (result)) {
@ -230,7 +230,7 @@ int Bot::findBestGoalWhenBombAction () {
}
}
else if (!m_defendedBomb) {
const auto &bombOrigin = graph.getBombOrigin ();
const auto &bombOrigin = gameState.getBombOrigin ();
if (!bombOrigin.empty ()) {
m_defendedBomb = true;
@ -239,7 +239,7 @@ int Bot::findBestGoalWhenBombAction () {
const auto &path = graph[result];
const float bombTimer = mp_c4timer.as <float> ();
const float timeMidBlowup = bots.getTimeBombPlanted () + (bombTimer * 0.5f + bombTimer * 0.25f) - graph.calculateTravelTime (pev->maxspeed, pev->origin, path.origin);
const float timeMidBlowup = gameState.getTimeBombPlanted () + (bombTimer * 0.5f + bombTimer * 0.25f) - graph.calculateTravelTime (pev->maxspeed, pev->origin, path.origin);
if (timeMidBlowup > game.time ()) {
clearTask (Task::MoveToPosition); // remove any move tasks
@ -288,14 +288,15 @@ int Bot::findGoalPost (int tactic, IntArray *defensive, IntArray *offensive) {
else if (tactic == GoalTactic::Goal && !graph.m_goalPoints.empty ()) { // map goal node
// force bomber to select closest goal, if round-start goal was reset by something
if (m_isVIP || (m_hasC4 && bots.getRoundStartTime () + 20.0f < game.time ())) {
if (m_isVIP || (m_hasC4 && gameState.getRoundStartTime () + 20.0f < game.time ())) {
float nearestDistanceSq = kInfiniteDistance;
int count = 0;
for (const auto &point : graph.m_goalPoints) {
const float distanceSq = graph[point].origin.distanceSq (pev->origin);
if (distanceSq > cr::sqrf (1024.0f) || isGroupOfEnemies (graph[point].origin)) {
if (distanceSq > cr::sqrf (1024.0f)
|| (rg.chance (25) && isGroupOfEnemies (graph[point].origin))) {
continue;
}
if (distanceSq < nearestDistanceSq) {
@ -594,7 +595,7 @@ void Bot::doPlayerAvoidance (const Vector &normal) {
void Bot::checkTerrain (const Vector &dirNormal) {
// if avoiding someone do not consider stuck
// if avoiding someone do not consider stuckn
TraceResult tr {};
m_isStuck = false;
@ -602,7 +603,7 @@ void Bot::checkTerrain (const Vector &dirNormal) {
// minimal speed for consider stuck
const float minimalSpeed = isDucking () ? kMinMovedDistance : kMinMovedDistance * 4;
const auto randomProbeTime = rg (0.75f, 1.15f);
const auto randomProbeTime = rg (0.50f, 0.75f);
// standing still, no need to check?
if ((cr::abs (m_moveSpeed) >= minimalSpeed || cr::abs (m_strafeSpeed) >= minimalSpeed)
@ -900,10 +901,7 @@ void Bot::checkTerrain (const Vector &dirNormal) {
}
void Bot::checkFall () {
if (isPreviousLadder ()) {
return;
}
else if ((m_pathFlags & NodeFlag::Ladder) && isPreviousLadder () && isOnLadder ()) {
if (isPreviousLadder () || (m_pathFlags & NodeFlag::Ladder)) {
return;
}
@ -1105,7 +1103,7 @@ bool Bot::updateNavigation () {
if (m_desiredVelocity.length2d () > 0.0f) {
pev->velocity = m_desiredVelocity;
}
else {
else if (graph.isAnalyzed ()) {
auto feet = pev->origin + pev->mins;
auto node = Vector { m_pathOrigin.x, m_pathOrigin.y, m_pathOrigin.z - ((m_pathFlags & NodeFlag::Crouch) ? 18.0f : 36.0f) };
@ -1151,7 +1149,7 @@ bool Bot::updateNavigation () {
}
if (m_pathFlags & NodeFlag::Ladder) {
constexpr auto kLadderOffset = Vector (0.0f, 0.0f, 36.0f);
constexpr auto kLadderOffset = Vector { 0.0f, 0.0f, 36.0f };
const auto prevNodeIndex = m_previousNodes[0];
const float ladderDistance = pev->origin.distance (m_pathOrigin);
@ -1231,7 +1229,7 @@ bool Bot::updateNavigation () {
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)) {
if (!game.isNullEntity (tr.pHit) && game.isNullEntity (m_liftEntity) && game.isDoorEntity (tr.pHit)) {
const auto &origin = game.getEntityOrigin (tr.pHit);
const float distanceSq = pev->origin.distanceSq (origin);
@ -1285,7 +1283,7 @@ bool Bot::updateNavigation () {
util.findNearestPlayer (reinterpret_cast <void **> (&nearest), ent (), 256.0f, false, false, true, true, false);
// check if enemy is penetrable
if (util.isAlive (nearest) && isPenetrableObstacle (nearest->v.origin) && !cv_ignore_enemies) {
if (game.isAliveEntity (nearest) && isPenetrableObstacle (nearest->v.origin) && !cv_ignore_enemies) {
m_seeEnemyTime = game.time ();
m_states |= Sense::SeeingEnemy | Sense::SuspectEnemy;
@ -1324,7 +1322,7 @@ bool Bot::updateNavigation () {
}
}
float desiredDistanceSq = cr::sqrf (8.0f);
float desiredDistanceSq = cr::sqrf (32.0f);
const float nodeDistanceSq = pev->origin.distanceSq (m_pathOrigin);
// initialize the radius for a special node type, where the node is considered to be reached
@ -1336,7 +1334,7 @@ bool Bot::updateNavigation () {
// on cs_ maps goals are usually hostages, so increase reachability distance for them, they (hostages) picked anyway
if (game.mapIs (MapFlags::HostageRescue) && (m_pathFlags & NodeFlag::Goal)) {
desiredDistanceSq = cr::sqrf (128.0f);
desiredDistanceSq = cr::sqrf (96.0f);
}
}
else if (m_pathFlags & NodeFlag::Ladder) {
@ -1374,7 +1372,7 @@ bool Bot::updateNavigation () {
}
// 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 (16.0f) && nodeDistanceSq < cr::sqrf (30.0f)) {
const auto predictRangeSq = m_pathOrigin.distanceSq (pev->origin + pev->velocity * m_frameInterval);
if (predictRangeSq >= nodeDistanceSq || predictRangeSq <= desiredDistanceSq) {
@ -1386,7 +1384,10 @@ bool Bot::updateNavigation () {
// is sitting there, so the bot is unable to reach the node because of other player on it, and he starts to jumping and so on
// here we're clearing task memory data (important!), since task executor may restart goal with one from memory, so this process
// will go in cycle, and forcing bot to re-create new route.
if (m_pathWalk.hasNext () && m_pathWalk.next () == m_pathWalk.last () && isOccupiedNode (m_pathWalk.next (), pathHasFlags)) {
if (m_pathWalk.hasNext ()
&& m_pathWalk.next () == m_pathWalk.last ()
&& isOccupiedNode (m_pathWalk.next (), pathHasFlags)) {
getTask ()->data = kInvalidNodeIndex;
m_currentNodeIndex = kInvalidNodeIndex;
@ -1418,7 +1419,7 @@ bool Bot::updateNavigation () {
const int taskTarget = getTask ()->data;
if (game.mapIs (MapFlags::Demolition)
&& bots.isBombPlanted ()
&& gameState.isBombPlanted ()
&& m_team == Team::CT
&& getCurrentTaskId () != Task::EscapeFromBomb
&& taskTarget != kInvalidNodeIndex) {
@ -1481,7 +1482,7 @@ bool Bot::updateLiftHandling () {
game.testLine (pev->origin, m_pathOrigin, TraceIgnore::Everything, ent (), &tr);
if (tr.flFraction < 1.0f
&& util.isDoorEntity (tr.pHit)
&& game.isDoorEntity (tr.pHit)
&& (m_liftState == LiftState::None || m_liftState == LiftState::WaitingFor || m_liftState == LiftState::LookingButtonOutside)
&& pev->groundentity != tr.pHit) {
@ -2117,7 +2118,7 @@ int Bot::findBombNode () {
const auto &goals = graph.m_goalPoints;
const auto &bomb = graph.getBombOrigin ();
const auto &bomb = gameState.getBombOrigin ();
const auto &audible = isBombAudible ();
// take the nearest to bomb nodes instead of goal if close enough
@ -2535,9 +2536,9 @@ bool Bot::advanceMovement () {
// only if we in normal task and bomb is not planted
if (tid == Task::Normal
&& bots.getRoundMidTime () + 5.0f < game.time ()
&& gameState.getRoundMidTime () + 5.0f < game.time ()
&& m_timeCamping + 5.0f < game.time ()
&& !bots.isBombPlanted ()
&& !gameState.isBombPlanted ()
&& m_personality != Personality::Rusher
&& !m_hasC4 && !m_isVIP
&& m_loosedBombNodeIndex == kInvalidNodeIndex
@ -2549,7 +2550,7 @@ bool Bot::advanceMovement () {
auto kills = static_cast <float> (practice.getDamage (m_team, nextIndex, nextIndex));
// if damage done higher than one
if (kills > 1.0f && bots.getRoundMidTime () > game.time ()) {
if (kills > 1.0f && gameState.getRoundMidTime () > game.time ()) {
switch (m_personality) {
case Personality::Normal:
kills *= 0.33f;
@ -2647,7 +2648,7 @@ bool Bot::advanceMovement () {
// mark as jump sequence, if the current and next paths are jumps
if (isCurrentJump) {
m_jumpSequence = willJump;
m_jumpSequence = willJump && jumpDistanceSq > cr::sqrf (96.0f);
}
// is there a jump node right ahead and do we need to draw out the light weapon ?
@ -2752,11 +2753,11 @@ bool Bot::isBlockedForward (const Vector &normal, TraceResult *tr) {
if (!game.mapIs (MapFlags::HasDoors)) {
return false;
}
return result->flFraction < 1.0f && !util.isDoorEntity (result->pHit);
return result->flFraction < 1.0f && !game.isDoorEntity (result->pHit);
};
auto checkHostage = [&] (TraceResult *result) {
return result->flFraction < 1.0f && m_team == Team::Terrorist && !util.isHostageEntity (result->pHit);
return result->flFraction < 1.0f && m_team == Team::Terrorist && !game.isHostageEntity (result->pHit);
};
// trace from the bot's eyes straight forward...
@ -2764,8 +2765,8 @@ bool Bot::isBlockedForward (const Vector &normal, TraceResult *tr) {
// check if the trace hit something...
if (tr->flFraction < 1.0f) {
if ((game.mapIs (MapFlags::HasDoors) && util.isDoorEntity (tr->pHit))
|| (m_team == Team::CT && util.isHostageEntity (tr->pHit))) {
if ((game.mapIs (MapFlags::HasDoors) && game.isDoorEntity (tr->pHit))
|| (m_team == Team::CT && game.isHostageEntity (tr->pHit))) {
return false;
}
return true; // bot's head will hit something
@ -3120,7 +3121,7 @@ bool Bot::isBlockedLeft () {
game.testLine (pev->origin, pev->origin - forward * direction - right * 48.0f, TraceIgnore::Monsters, ent (), &tr);
// check if the trace hit something...
if (game.mapIs (MapFlags::HasDoors) && tr.flFraction < 1.0f && !util.isDoorEntity (tr.pHit)) {
if (game.mapIs (MapFlags::HasDoors) && tr.flFraction < 1.0f && !game.isDoorEntity (tr.pHit)) {
return true; // bot's body will hit something
}
return false;
@ -3140,7 +3141,7 @@ bool Bot::isBlockedRight () {
game.testLine (pev->origin, pev->origin + forward * direction + right * 48.0f, TraceIgnore::Monsters, ent (), &tr);
// check if the trace hit something...
if (game.mapIs (MapFlags::HasDoors) && tr.flFraction < 1.0f && !util.isDoorEntity (tr.pHit)) {
if (game.mapIs (MapFlags::HasDoors) && tr.flFraction < 1.0f && !game.isDoorEntity (tr.pHit)) {
return true; // bot's body will hit something
}
return false;
@ -3302,7 +3303,7 @@ int Bot::getNearestToPlantedBomb () {
// search the bomb on the map
game.searchEntities ("classname", "grenade", [&] (edict_t *ent) {
if (util.isModel (ent, bombModel)) {
if (game.isEntityModelMatches (ent, bombModel)) {
result = graph.getNearest (game.getEntityOrigin (ent));
if (graph.exists (result)) {