Skip to content

Bots follow their path on NAV_MESH_PRECISE areas - #2168

Merged
sunzenshen merged 2 commits into
NeotokyoRebuild:masterfrom
sunzenshen:bot-nav-precise-careful
Sep 28, 2026
Merged

sunzenshen merged 2 commits into
NeotokyoRebuild:masterfrom
sunzenshen:bot-nav-precise-careful

Conversation

@sunzenshen

@sunzenshen sunzenshen commented Sep 26, 2026 •

Copy link
Copy Markdown
Contributor

Description

Bots strictly follow their path on NAV_MESH_PRECISE/CLIFF areas

NAV_MESH_PRECISE is the stock nav attribute for "do not adjust for obstacles, just move along area" (nav.h), set with nav_precise in the nav editor. Stock NextBot code honors it in two places: when a path is built, Path::FindNextOccludedNode does not optimize past a point in a PRECISE area, and PathFollower::Avoid does not steer around obstacles there. But the path follower's own skip-ahead still heads past that point once it is close, and the rest of a bot's movement ignores the attribute, so it can still carry the bot off the line the mesh author marked. That matters most on a ledge beside a fatal drop, which the per-map navmesh PRs mark NAV_MESH_PRECISE + NAV_MESH_CLIFF, and on a narrow crossing such as a doorway or the gap past a handrail's end.

On an area marked NAV_MESH_PRECISE or NAV_MESH_CLIFF, this PR keeps a bot on its path:

  1. The path follower does not skip past a point that enters a PRECISE area (NextBotPathFollow.cpp, #ifdef NEO). Skip-ahead normally heads for a later path point once the next one is close. This is the same rule the path builder already follows, applied to the follower. Without it the bot cuts the corner the author marked, then presses into the handrail or door frame it was meant to walk around.
  2. No sidestepping: no combat dodge (Dodge), no close-range circle-strafe (Attack), and no stepping straight away from a teammate within 32 u (AvoidBumpingFriends). Each of these moves the bot sideways without regard to what is beside it. A new CNEOBot::IsOnPreciseArea() gives them one check.

Where it applies

No mesh on master marks PRECISE or CLIFF today, so on its own this changes nothing on the current maps. Where the marks go is decided per map in each navmesh PR. Two open ones already carry them:

Testing this build with either PR's .nav shows the change. On redlight, stuck events on its PRECISE areas go from 1.9 to 0.3 a match.

Evidence

Bot matches with the research meshes, 30 bots, default weapon damage, CTG with the ghost pinned at 3 spawns (DM: unpinned), all arms interleaved in one batch. Temporary cvars switched each part in the test build only; this PR has no switches.

Each part against the whole PR (280 matches: skyline, apparatus, redlight, ballistrade, subsurface; this batch also had the exact move direction described below):

without stuck events (paired) fall deaths (paired)
1. the PRECISE waypoint x1.37 [1.12, 1.69] x1.02
2. no sidestepping x1.06 x0.99
  • Part 1 carries the stuck gain. Without it, bots on these maps get stuck 37 % more.
  • Part 2 is not measurable on its own, in this batch or an earlier one (falls and stuck events both within noise). It is a safety rule rather than a measured gain. A sideways step on a fall edge is exactly the move the marking exists to prevent, and it costs one attribute check on an area the bot already tracks.

A second batch compared the parts (with the exact move direction) against none on 10 maps (160 matches): stuck events on PRECISE areas 2.45 -> 1.24 a match (skyline 15.3 -> 7.0, redlight 1.9 -> 0.3), overall stuck and kills unchanged.

The PR alone on master's code (120 matches: upstream plus this PR, with logging added and a test-only switch; same maps plus bullet; the other bot research is absent, so stuck counts are much higher on these maps):

  • stuck events x0.94 [0.79, 1.12] paired
  • fall deaths 446 -> 392 (paired x0.95 [0.88, 1.04]); on skyline, falls from marked areas 3.6 -> 1.5 a match
  • stuck events on PRECISE areas 4.9 -> 4.5 a match
  • kills unchanged

That batch included the timer version of the exact move direction. On master's code the effect is mildly positive and within noise. Most stuck events there come from spots this PR does not touch, such as ladders.

The exact move direction, taken out

The first version of this PR also set the move input to the exact direction to the goal on these areas, using the stock SetButtonScale. In review, sunmachine pointed out that the scale lapses within a tick and that bot updates run every 6-13 ticks, so on most ticks the bot still moved on the eight-direction keys. They suggested holding the world direction until the next bot update and converting it against EyeAngles() every tick.

That design was built and measured against this branch without any move-input change (the timer version as context), on skyline, tarmac, redlight, ballistrade, bullet and apparatus, 30 bots, harness logging added:

timer version (120 matches an arm) sunmachine's design, batch 1 (120) sunmachine's design, batch 2 (64)
fall deaths x0.90 [0.79, 1.00] x0.79 [0.64, 0.93] x0.69 [0.59, 0.77]
falls from PRECISE / CLIFF areas x0.87 [0.70, 1.05] x0.75 [0.58, 0.93] x0.66 [0.51, 0.78]
stuck events on PRECISE / CLIFF areas x1.10 [0.82, 1.48] x1.84 [1.34, 2.55] x1.42 [0.92, 2.27]
stuck events, whole match x0.89 x1.01 x1.02

The direction held between updates cuts falls clearly, but bots on a ledge then push into each other instead of sliding past (teammate-blocked stuck events on those areas went 38 -> 135). Falling back to the keys behind another player, or while stuck, did not lower that. So the move-input change is out of this PR. sunmachine's design is in its own draft PR, #2175, where the ledge crowding can be worked on without holding this one up.

Cost

One attribute test on the bot's last known area in each place, which is already cached; nothing is traced.

Smoke

The current head with harness logging added: 12 matches on redlight, ballistrade, skyline, tarmac, bullet and apparatus, all ended cleanly, 0 asserts, no crash dumps.

Risk

Low. Only meshes that mark PRECISE or CLIFF are affected. The strongest effect, the waypoint, only changes which path point a bot heads for, never the path itself.

Toolchain

  • Linux GCC Distro Native (Linux Mint 22.3, GCC 13.3.0)

Linked Issues

@sunzenshen
sunzenshen force-pushed the bot-nav-precise-careful branch from 2977330 to 4d619ac Compare September 27, 2026 05:20
@sunzenshen
sunzenshen marked this pull request as ready for review September 27, 2026 05:21
On an area marked NAV_MESH_PRECISE or NAV_MESH_CLIFF:
- the path follower does not skip past a point that enters a PRECISE
  area, as the path builder already does not
- move input follows the exact direction to the goal instead of the
  nearest of eight key combinations
- no combat dodge, no close-range circle-strafe and no stepping away
  from teammates, any of which can carry a bot off a ledge

No mesh on master marks PRECISE or CLIFF today.
@sunzenshen
sunzenshen force-pushed the bot-nav-precise-careful branch from 4d619ac to 086ffc1 Compare September 27, 2026 05:21
@sunzenshen sunzenshen added the Bots Related to bot players label Sep 27, 2026
Review (sunmachine) showed the button scale it relied on lapses within
a tick, and bot updates run every 6-13 ticks, so on most ticks the bot
still moved on the eight-direction keys.

Their design - hold the world direction until the next bot update and
convert it against the view every tick - was measured against this
branch without any move-input change: fall deaths x0.79 and x0.69 in
two batches, but stuck events on PRECISE / CLIFF areas x1.84 and x1.42,
with bots on a ledge now pushing into each other instead of sliding
past. That trade needs more work, so it moves to its own draft PR and
this one keeps the waypoint and the no-sidestep rules.

@sunmachine sunmachine left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Holding approval until we figure this part out.

Comment thread src/game/server/NextBot/Player/NextBotPlayerLocomotion.cpp Outdated
@sunzenshen sunzenshen changed the title Bots walk NAV_MESH_PRECISE areas exactly Bots follow their path on NAV_MESH_PRECISE areas Sep 27, 2026
@sunzenshen
sunzenshen merged commit 2a132c9 into NeotokyoRebuild:master Sep 28, 2026
7 checks passed
sunzenshen added a commit to sunzenshen/neo that referenced this pull request Oct 3, 2026
Bot movement presses the nearest of eight key combinations, up to 22.5
degrees off the goal, so a bot looking elsewhere drifts sideways - on a
ledge marked NAV_MESH_PRECISE or NAV_MESH_CLIFF, off the edge.

On those areas Approach() now hands the player the world direction to
the goal. PhysicsSimulate() applies it on every tick until the next bot
update, converted against that tick's view, so a bot turning to aim
keeps its line. The stock SetButtonScale() does not do this: its 0.01 s
timer lapses within a tick and it is relative to the view at update
time. Design by sunmachine (review of NeotokyoRebuild#2168).

Draft: measured falls down (x0.79, x0.69), but stuck events on these
areas up (x1.84, x1.42) as bots on a ledge push into each other.
sunzenshen added a commit to sunzenshen/neo that referenced this pull request Oct 5, 2026
Bot movement presses the nearest of eight key combinations, up to 22.5
degrees off the goal, so a bot looking elsewhere drifts sideways - on a
ledge marked NAV_MESH_PRECISE or NAV_MESH_CLIFF, off the edge.

On those areas Approach() now hands the player the world direction to
the goal. PhysicsSimulate() applies it on every tick until the next bot
update, converted against that tick's view, so a bot turning to aim
keeps its line. The stock SetButtonScale() does not do this: its 0.01 s
timer lapses within a tick and it is relative to the view at update
time. Design by sunmachine (review of NeotokyoRebuild#2168).

Draft: measured falls down (x0.79, x0.69), but stuck events on these
areas up (x1.84, x1.42) as bots on a ledge push into each other.
sunzenshen added a commit to sunzenshen/neo that referenced this pull request Oct 7, 2026
Bot movement presses the nearest of eight key combinations, up to 22.5
degrees off the goal, so a bot looking elsewhere drifts sideways - on a
ledge marked NAV_MESH_PRECISE or NAV_MESH_CLIFF, off the edge.

On those areas Approach() now hands the player the world direction to
the goal. PhysicsSimulate() applies it on every tick until the next bot
update, converted against that tick's view, so a bot turning to aim
keeps its line. The stock SetButtonScale() does not do this: its 0.01 s
timer lapses within a tick and it is relative to the view at update
time. Design by sunmachine (review of NeotokyoRebuild#2168).

Draft: measured falls down (x0.79, x0.69), but stuck events on these
areas up (x1.84, x1.42) as bots on a ledge push into each other.
sunzenshen added a commit to sunzenshen/neo that referenced this pull request Oct 8, 2026
Bot movement presses the nearest of eight key combinations, up to 22.5
degrees off the goal, so a bot looking elsewhere drifts sideways - on a
ledge marked NAV_MESH_PRECISE or NAV_MESH_CLIFF, off the edge.

On those areas Approach() now hands the player the world direction to
the goal. PhysicsSimulate() applies it on every tick until the next bot
update, converted against that tick's view, so a bot turning to aim
keeps its line. The stock SetButtonScale() does not do this: its 0.01 s
timer lapses within a tick and it is relative to the view at update
time. Design by sunmachine (review of NeotokyoRebuild#2168).

Draft: measured falls down (x0.79, x0.69), but stuck events on these
areas up (x1.84, x1.42) as bots on a ledge push into each other.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Bots Related to bot players

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants