Skip to content

Stop navgen seating waypoints past a ledge lip - #250

Merged
saworbit merged 1 commit into
mainfrom
navgen-checkbottom-seats
Sep 4, 2026
Merged

Stop navgen seating waypoints past a ledge lip#250
saworbit merged 1 commit into
mainfrom
navgen-checkbottom-seats

Conversation

@saworbit

@saworbit saworbit commented Sep 4, 2026

Copy link
Copy Markdown
Owner

navgen samples hull 1, the player clip hull. That is the world expanded by the player box, so its floor reaches half a player width past every real ledge edge. The sampler read that as standable ground.

On e1m6 it seated n93 at -207 305 24. The true lip at that y is x -193, so the seat sat 14 units out over a 304 unit drop with lava at -128.

x hull 1 floor, what navgen samples hull 0 floor, the real world
-215 -280 -306
-207 24 -306
-199 24 -306
-191 24 -2

The engine never agreed. SV_movestep calls SV_CheckBottom, which point traces the box corners against the world, so walkmove refuses the step and Argus_MoveHazard refuses it too. A bot routed onto such a seat can neither arrive nor give up. A co-op companion on e1m6 spent its whole session at that lip: 213 stalls and 215 jumps in 175 s, mode 0 on 310 of 327 samples, three goals, never a death.

The fix

Section 4c runs the engine's own test instead of a guess at one, with the same constants and the same order. Corners solid just under the feet is the fast accept. Otherwise the midpoint must find floor within 2 * STEP, and every corner must sit within STEP of it.

e1m6, regenerated

nodes seat by the lava lip seats over a 96 unit void worst spawn reach
before 198 -207 305 24 32, 11 of them over lava 85%
after 188 -175 305 24 0 95%

536 lip overhang samples dropped. The seat moved onto real ledge rather than disappearing. Stalls ran 220, 213 and 200 across three tapes on the old graph, and 6, 2 and 5 on the new one.

Other maps are not regenerated

Those graphs are accumulated and tuned, so each wants its own ladder. Dry runs confirm the rule does not fragment them.

map seats dropped worst spawn reach
dm4 212 98%
dm6 209 95%
e1m1 501 99%

dm4 in engine, 180 s: cells 75, stalls 18, routefails 9, no freezes, against 72 / 51 / 17 on the pre-change binary.

Honest residual

e1m6 is still not co-op ready, and this change does not make it so. The old graph's only route to the gold key crossed the lava pit through n93. Removing the fake seat removes the fake route, so routefails run 400 per tape where they ran 9. That crossing is a func_door which rises on a trigger_once chain and stays up, and in a botmatch nothing fires it, so the route genuinely does not exist at runtime. Modelling the mover in navgen would only move the lie to a different height. What e1m6 needs is level progression handling, which is much larger work. Tracked on #249.

The trade this PR takes is a wedged bot for an honest graph. Stalls fall from about 210 to about 5, coverage moves 13 cells to 9, and routefails rise. By the project's own gate order that is the right way round, but the routefail rate is worth watching on a multi bot server, since an_busy serialises the router.

Tried and reverted, each on its own ladder

  • A swimmer reach ceiling measured from the water surface rather than the bot. Offline it looked decisive, dm3 drag-down 45% of wet samples to 0%. It readmitted 1585 1185 208, the exact seat the graveyard entry names. Both that seat and dm3's lip sit 36 above their surface, so no height rule separates them.
  • A reach test on the Argus_NearestNode fast path. It is a real hole, the neighbour loop applies no reach test and returns early at 120 units. It improved dm3 but cost e1m2 co-op half its coverage, 55 cells to 24 on two tapes.
  • A routefail shelf on the co-op key commitment. It did not touch the storm, because the retries come from the goal already on the stack rather than from re-commitment, and it cost e1m2 routefails 0 to 77 and stalls 40 to 73.

Refs #249

🤖 Generated with Claude Code

navgen samples hull 1, the player clip hull. That is the world
expanded by the player box, so its floor reaches half a player width
past every real ledge edge, and the sampler read that as standable
ground.

On e1m6 it seated n93 at '-207 305 24'. The true lip at that y is
x -193, so the seat sat 14 units out over a 304 unit drop with lava
at -128. Measured along y 305, hull 1 reports floor at z 24 from
x -207 eastward while the world floor is -306 until x -191.

The engine never agreed. SV_movestep calls SV_CheckBottom, which
point traces the box corners against the world, so walkmove refuses
the step, and Argus_MoveHazard refuses it too. A bot routed onto such
a seat can neither arrive nor give up. A co-op companion on e1m6
spent its whole session at that lip: 213 stalls and 215 jumps in
175 s, mode 0 on 310 of 327 samples, three goals, never a death.

So run the engine's own test rather than a guess at one. Section 4c
applies SV_CheckBottom against the world with the same constants and
the same order. Corners solid just under the feet is the fast accept.
Otherwise the midpoint must find floor within 2 * STEP, and every
corner must sit within STEP of it.

e1m6 regenerated. 536 lip overhang samples dropped, 198 nodes to 188,
the lip seat relocated from '-207 305 24' to '-175 305 24' on real
ledge, seats over a 96 unit void 32 to 0, worst spawn reach 85 to
95 per cent. Stalls ran 220, 213 and 200 across three tapes on the
old graph, and 6, 2 and 5 on the new one.

Other maps are NOT regenerated. Those graphs are accumulated and
tuned and each wants its own ladder. Dry runs confirm the rule does
not fragment them: dm4 98 per cent worst spawn reach with 212 seats
dropped, dm6 95 per cent with 209, e1m1 99 per cent with 501.

HONEST RESIDUAL, and the reason e1m6 is still not co-op ready. The
old graph's only route to the gold key crossed the lava pit THROUGH
n93. Removing the fake seat removes the fake route, so routefails run
400 per tape where they ran 9. That crossing is a func_door which
rises on a trigger_once chain and stays up, and in a botmatch nothing
fires it, so the route genuinely does not exist at runtime. Modelling
the mover in navgen would only move the lie to a different height.

Three things tried and reverted on their own ladders, each with its
post mortem on the issue. A swimmer reach ceiling measured from the
water surface, which readmitted the exact seat the graveyard names.
A reach test on the Argus_NearestNode fast path, which is a real hole
but cost e1m2 co-op half its coverage, 55 cells to 24 on two tapes.
A routefail shelf on the co-op key commitment, which did not touch
the storm because the retries come from the goal already on the
stack, and cost e1m2 routefails 0 to 77 and stalls 40 to 73.

Refs #249

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant