Skip to content

ntre_disengage_ctg: navmesh rework - #153

Merged
sunzenshen merged 4 commits into
NeotokyoRebuild:masterfrom
sunzenshen:navmesh-disengage-release
Oct 14, 2026
Merged

sunzenshen merged 4 commits into
NeotokyoRebuild:masterfrom
sunzenshen:navmesh-disengage-release

Conversation

@sunzenshen

@sunzenshen sunzenshen commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

ntre_disengage_ctg: navmesh rework

One file, neo/maps/nav/ntre_disengage_ctg.nav. It was measured on upstream master plus the draft NeotokyoRebuild/neo#2176 (then only its unwanted-ladder release, now its changes 1 and 7) and NeotokyoRebuild/neo#2186 ("Bots skip weapons they cannot reach", merged since), with harness logging added. ntre_engage_ctg shares most of this layout and gets its own PR (#152).

Dependencies and status

Needs (worse than master, or broken, without it):

Helped by (measured better with it; the mesh does not wait on it):

  • None measured.

Pending before review:

  • Nothing: ready for review.

At a glance

master this PR
nav areas 1966 1489
file size 3,328,400 bytes 2,033,046 bytes (-39 %)
areas the engine blocks at every round start 25 1 (see below)
areas no spawn can reach 115 in 27 pockets 109 in 22 pockets, all left by dropping (see below)
areas that can be entered and not left, for some class 3 (the staircase's lower landing) 0
nav ladders 2 2, both ends linked both ways, as on master
bot stuck events a match (see below) 14.8 4.3

Four commits:

  1. Rework - the mesh itself.
  2. Id alignment with master - ids only, so an id-keyed diff against master reads sensibly.
  3. Review fixes - the balcony staircase's four flights, one-way walk links, and climbs no bot makes, from an outside review (section 3).
  4. Blocked links - drop backs restored, and areas no player hull fits in removed, from the engine's hull audit of commit 3 (section 3, last part).

1. What changed

  • Sliver areas merged into their neighbors on the same floor, props carved out of standing room, and areas the engine blocks at round start (a prop or brush over their center) cut away. On master the engine blocks 25 areas at every round start; here 1.
  • The leaning sheetrock near (-140,1580) (a prop_dynamic): master's areas ran under it, and bots crouched into it. It was master's largest stuck spot, 5.7 stuck events a match in 17 of 24 matches. The areas under it are carved away. Here: 0.
  • The car near (1025,60): master's areas ran under it; carved away (master 1.5 stuck events a match beside it, here 0).
  • The rooftop room of ghost spawn 2 (1467,214) had exits and no entry: the mesh stopped 25 u short of its doorway on both sides. A 24 u strip through the doorway gives it one.
  • Pockets no spawn can reach and nothing links into are removed, and so is a strip near (-400,638) with no way out.
  • Re-analyzed in game after the edits, so the per-area visibility lists bots use for what they can see are current.

2. Id alignment

1488 of the 1491 areas carry the id of the master area they match. Both ladders keep their ids. Areas are matched only within 64 u of height. Geometry, connections, attributes and the analysis data are unchanged by this step (verified topology-identical).

3. Review fixes

  • The balcony staircase. The map has four flights of stairs from the street up to the balcony beside the rooftop room, the same stair model in the same place as on ntre_engage_ctg. Master's mesh, and this PR's until now, had none of them: the landings were joined straight across each flight by 76 u walk links, which no class can climb, so the lowest landing (2116 / 9587) could be dropped onto and not left. The four flights are now meshed the way ntre_engage_ctg's PR (ntre_engage_ctg: navmesh rework #152) meshes them: one sloped area per flight from the lower landing's edge to the upper's, a strip from the street to the first flight, the middle landing split so bots turn clear of the stringer ends, and the two west landings shaped the same way. The 76 u links are gone. The engine marks the four flights STAIRS when it loads the mesh. The nine new areas are numbered above master's highest id; the landings keep their ids.
  • The roof walkway's drop over its north parapet (12471 -> 1450) is cut, as in ntre_engage_ctg's PR: with the stairs, bots reach the rooftop room on foot, and a bot following that drop walks into the parapet.
  • One-way walk links. An earlier pass that cut walk links over railings and props cut one direction of some pairs and kept the other, so a bot following the kept direction walked into the same obstacle. Each pair was swept by the engine with a player hull in both directions:
    • blocked both ways, now cut both ways: eight pairs, at the handrails of the stairwell near (2750,300) (6), a street lamp (1) and the truck near (-40,1550) (1, one-way on master)
    • also cut both ways: 1692 / 6207, a link across 50 u of floor that a forklift and its raised pallets stand on, near (25,262), one-way on master (the hull sweep tests only the two ends of such a link)
    • clear both ways, the missing direction restored: nine pairs, among them two narrow ones beside street blockades (6882 / 6883, 5943 / 5784)
  • Climbs no bot makes. Six vehicle roofs, a trash compactor and an awning ledge near (-320,240) (35 areas) were reached only by walk climbs of 71-120 u, above every class's limit (a Recon plans climbs under 68 u). Those nine climbs are removed; the areas stay as pockets bots can drop from, like the other roof pockets this mesh keeps. In the map geometry a chain of crouch jumps of 40 u or less over bumper and hood reaches three of the vehicle roofs; bots are not given that route here.
  • 3177, a floor strip with no way out in an unreached pocket east of the map, is deleted.
  • Blocked links (commit 4). The engine's hull audit of commit 3 (a player hull stood on every area and swept across every link, in the server), and a link check against master:
    • eight climbs of 19-47 u had lost their drop back to the railing pass; the climb reads clear, so the drop back is restored (one of them, 12793 -> 12790, was one-way on master)
    • areas no player hull stands in, every stand sample blocked, are deleted: the two squares inside the wrecked mech near (838,1750) (bots heading into them stuck there; the mech's top becomes a pocket bots can drop from, like the vehicle roofs), a 29 x 6 u strip on the wall trim beside the walkway stairs near (1340,372) (reached only by a climb blocked on every lane), two street-lamp squares, a scooter square, a car square and two corner squares near (1850,630)
    • three pairs blocked both ways that bots never cross are cut both ways: a scaffold strip, a stair handrail, a double post
    • kept: three links the audit reads blocked on displacement slopes, which bots cross 15-35 times in 40 matches; the recycle bin near (2450,812), a physics prop, left as it stands at map load for the bot code's runtime prop avoidance

How it was tested

  • Reachability:
    • every spawn can reach every other spawn
    • every objective is reachable
    • 109 areas no spawn can reach, in 22 pockets, all of which lead back into the mesh or are sealed: ledge, roof and vehicle-roof pockets bots can only drop from (master has 115 in 27 pockets). 35 of them were reached before section 3 only by climbs of 71-120 u, which no class plans; 3 (the wrecked mech's top) before commit 4 only through the squares inside the mech
    • no area that can be entered and not left, for any class, counting only the climbs each class plans (Recon under 68 u, Assault 48 u, Support 43 u); master has one, the staircase's lowest landing
    • no walk link between two areas at the same level runs one way, and no climb a class plans lacks its drop back
    • the engine hull audit of this mesh: of 2645 pairs of areas at the same height, none is blocked both ways by a wall or a static prop but three on displacement slopes that bots cross; one by a physics prop (BLOCKED_LINKS map=ntre_disengage_ctg level_pairs=2645 oneway=0 keep=0 cut=0 area=0 check=3 entity=1)
    • the engine blocks 1 area at every round start (master: 25): 12519, in a roof pocket near (-460,340) that no spawn reaches; master blocks it as well
  • Falls. The map has no trigger_hurt; its env_fire does 1 damage a second. No drop kills a bot at full health (the deepest drop link is 221 u, 1 damage). No fall marks were needed.
  • Master against this mesh, same bot code. Byte-identical copies of the map, one carrying each .nav, run back to back. CTG, 2 rounds of 3 minutes with the ghost pinned at each of the 8 ghost spawns in turn, 3 runs each, 30 bots, default weapon damage, 8 matches at a time. Every match reached match end with 0 asserts and no crash (598 matches on engage and disengage in all, probes and the other revisions included). The stuck count is OnStuck plus FAIL_STUCK move failures, excluding the spawn crush at round start and the weapon-scavenging behavior.
    • stuck events 14.8 -> 4.3 a match (paired x0.27, 95 % interval [0.16, 0.48], 19 of 24 pairs down, 4 up)
    • without #2176 (the unwanted-ladder release switched off in both arms, so upstream ladder code; 4 runs a spawn): 13.7 -> 5.0 (x0.57 [0.32, 0.98], 21 of 32 pairs down, 11 up)
    • FAIL_NO_PATH_EXISTS move failures 17.3 -> 17.7 a match (x1.02 [0.71, 1.46]); about three quarters of them on both meshes are bots attacking toward an area the bot code marks hazardous, which the path cost refuses; no "fell off the path" failures on either mesh
    • kills 50.6 -> 50.3 a match (x0.99 [0.96, 1.03]); ghost captures 0 of 67 rounds -> 2 of 61 (rounds on this map mostly end by elimination); team kill ratio (Jinrai / NSF enemy kills) 1.01 -> 1.00; no deaths to the world
    • bots still see and shoot across the mesh: 99 % of kills are between bots in different nav areas (master 98 %), 93 % in non-adjacent areas (master 95 %); median kill distance 830 u (master 816 u)
    • at the spots: the leaning sheetrock 5.7 -> 0 stuck events a match; the car near (1025,60) 1.5 -> 0
  • The review fixes (section 3) against the previous revision of this PR, one batch. The same twin setup with the previous and the fixed .nav, the ghost pinned at each of the 8 ghost spawns, 5 matches each, 30 bots, default weapon damage, 8 matches at a time, on the same bot code as above:
    • stuck events 6.9 -> 5.0 a match (paired x0.71, 95 % interval [0.48, 1.05], 21 of 40 pairs down, 14 up)
    • stuck events at the ends of the edited links 19 -> 8 in 40 matches (at the forklift 10 -> 5); none on the staircase or the roof walkway (1 on the walkway before)
    • the rooftop room's ghost spawn (1467,214): the ghost was picked up in 8 of 10 rounds, about 30 s after the round started, against 1 of 10 before, when the room was reached only by the two ladders. Bots now walk the stairs 0.25 times a match, mostly ghost carriers coming down from the room
    • FAIL_NO_PATH_EXISTS move failures 17.9 -> 17.0 a match; kills 50.3 -> 50.0 a match; ghost captures 8 -> 3 of 105 rounds (6 of the 8 at one ghost spawn, (2456,1042); this mesh's earlier batches had 4 in 146 rounds); 99 % of kills between different nav areas and 94 % between non-adjacent ones on both
    • no fall deaths, no asserts, no crashes in 80 matches
  • Commit 4 against commit 3, one batch. The same twin setup, the ghost pinned at each of the 8 ghost spawns, 3 matches each, 30 bots, default weapon damage, 8 matches at a time, on the same bot code:
    • stuck events 5.6 -> 3.9 a match (paired x0.74, 95 % interval [0.46, 1.20], 14 of 24 pairs down, 9 up); no stuck event at the edited links or areas on either
    • FAIL_NO_PATH_EXISTS move failures 16.5 -> 18.0 a match; kills 50.4 -> 52.3 a match; ghost captures 1 -> 3 of 48 rounds; 99 / 98 % of kills between different nav areas and 94 % between non-adjacent ones on both
    • no fall deaths, no asserts, no crashes in 48 matches
  • Staircase probes. The ghost pinned on each of the four landings, 3 matches each, 12 bots, a tenth of weapon damage: reached on 12 of 12 (first bot after 45-96 s), against 5 of 12 before (169-275 s), when the landings could only be reached by dropping across a flight from the top.

How the pinned-ghost probes work

The offline checks show that the mesh links an area to a spawn. They do not show that a bot will walk there. To test one spot in game, the ghost is placed on it and bots are sent to get it.

  • The ghost is placed on the spot by a server cvar. A harness-only neo patch, used on test builds and not part of this PR or any neo PR, adds sv_neo_ghost_spawn_pos "x y z", which replaces the position of the round's ghost spawn. The map file is not edited.
  • A spot counts as reached when a bot stands within 40 u of it, or picks the ghost up there rather than on the floor below, where a ghost that rolls off a narrow landing ends up. A wider radius would count a bot on a level above or below.
  • Master's mesh runs beside this one on a byte-identical copy of the .bsp under a second name that loads its own .nav, so the same pin runs on both meshes in one batch.

On this map

Both ends of both ladders were probed (every landing at ladder 2's top), 3 matches each, 12 bots, at a tenth of weapon damage, on the same build.

probe master this PR
ladder 1 bottom 3 of 3 3 of 3
ladder 1 top 2 of 3 2 of 3
ladder 2 bottom 3 of 3 3 of 3
ladder 2 top (all landings) 9 of 9 6 of 6

No bot climbed either ladder in normal play on either mesh (40 matches each). Ghost spawn 6 (see "Known, not changed") was probed the same way, 9 matches each: picked up in 8 of 9 on master and 7 of 9 here, always on the floor under the deck, after 126-535 s on both.

Known, not changed

  • A wall corner near (1750,2715): bots coming south into area 230 and turning west cut the corner and press into it, 3 rounds of 61 (master 0 of 67). The areas and links there are the same as master's.
  • Ghost spawn 6 (133,-145): the ghost falls off the tilted deck to the floor under it, and bots take 150-250 s to pick it up, on master as on this mesh (both mesh that spot the same way). That is bot code for a ghost bots cannot walk onto, not the mesh.

One file. Against master: 1966 -> 1491 areas, 3,328,400 -> 2,047,151
bytes; areas the engine blocks at every round start 25 -> 1; areas
no spawn can reach 115 -> 72.

- Sliver areas merged into their neighbors on the same floor, props
  carved out of standing room, and areas the engine blocks at round
  start (a prop or brush over their center) cut away. Among them the
  areas under the leaning sheetrock near (-140,1580), master's
  largest stuck spot, and under the car near (1025,60).
- The rooftop room of ghost spawn 2 (1467,214) had exits and no
  entry: the mesh stopped 25 u short of its doorway on both sides.
  A 24 u strip through the doorway gives it one.
- Pockets no spawn can reach and nothing links into are removed, and
  so is a strip near (-400,638) with no way out.
- Re-analyzed in game after the edits.
Renumbers area and ladder ids only, so an id-keyed diff against
master reads sensibly: 1488 of the 1491 areas and both ladders carry
the id of the master area they match. Areas are matched only within
64 u of height. Geometry, connections, attributes and the analyzed
visibility data are unchanged (verified topology-identical).
An outside review of this mesh found the lower landing of the balcony
staircase (2116 / 9587) to be a dead end. The map has four flights of
stairs there, the same model in the same place as on ntre_engage_ctg,
but the mesh had none of them: only 76 u links straight across each
flight, which no class can climb. The review also found walk links
that run one way only, and roof pockets entered only by climbs no bot
plans.

- Mesh the four flights as on ntre_engage_ctg: one sloped area per
  flight, a strip from the ground to the first flight, the middle
  landing split at x 1640 and the two west landings shaped so each
  U-turn is made clear of the stringer ends. The three 76 u links go.
  The engine marks the four flights STAIRS.
- Cut the roof walkway's drop over its north parapet (12471 -> 1450),
  as on ntre_engage_ctg: with the stairs, bots reach the rooftop room
  on foot, and a bot following that drop walks into the parapet.
- One-way links, each swept with a player hull in both directions by
  the engine's hull audit: cut both ways the eight pairs blocked both
  ways (interior stair handrails, a street lamp, the truck) and
  1692 / 6207, which spans 50 u of floor at a forklift; restore the
  missing direction of nine pairs that are clear both ways.
- Remove the nine climbs of 71-120 u that were the only way onto six
  vehicle roofs, a trash compactor and an awning (35 areas). No class
  plans a climb that high; the areas stay as drop-only pockets.
- Delete 3177, a strip with no way out in the unreached east pocket.

Existing areas keep their ids; the nine new ones are numbered above
master's highest. Re-analyzed in game (same graph, fresh visibility).
The engine's hull audit (a player hull stood on every area and swept
across every link, in the server) of the previous revision, and
navconnect's link check against master:

- Restore the drop back of eight climbs whose drop had been cut: the
  climb reads clear on some or all lanes. One of them (12793 ->
  12790) was one-way on master too.
- Delete 12492, a 29 x 6 u strip on the wall trim beside the walkway
  stairs at (1340, 372): no hull stands on it, and its only way in was
  a 26 u climb blocked on every lane.
- Delete areas no player hull stands in, every stand sample blocked:
  the two squares inside the wrecked mech at (838, 1750) (bots heading
  into them stuck there; the mech top becomes a drop-only pocket like
  the vehicle roofs), two street-lamp squares, a scooter square, a car
  square, two corner squares behind a wall at (1850, 630).
- Cut three pairs blocked both ways that bots never cross: a scaffold
  strip, a stair handrail, a double post.

Kept: links the audit reads blocked on displacement slopes, which
bots cross; the recycle bin near (2450, 812), a physics prop, left as
at map load.

Existing areas keep their ids. Re-analyzed in game (same graph,
fresh visibility).
@sunzenshen
sunzenshen requested a review from sunmachine October 9, 2026 05:26
@sunzenshen
sunzenshen marked this pull request as ready for review October 9, 2026 05:27

@sunmachine sunmachine left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Well, my screenshot has the world's tiniest arrowheads because I messed with how this stuff scales on retina devices (😭), but the gist is that, at least for this section of railing, there's no link anymore that allows for jumping up on this railing. That may be fine, but I didn't see it detailed anywhere in the PR so I'll assume it's unintentional.

So why am I approving? Because bots can still hop up on other sides of the railing, and maybe it's actually desirable at this position to keep the bots from jumping up. It actually kinda' makes sense for this part of the map.

Screenshot 2026-10-13 at 19 51 49 Image

@sunzenshen

Copy link
Copy Markdown
Contributor Author

Well, my screenshot has the world's tiniest arrowheads because I messed with how this stuff scales on retina devices (😭), but the gist is that, at least for this section of railing, there's no link anymore that allows for jumping up on this railing. That may be fine, but I didn't see it detailed anywhere in the PR so I'll assume it's unintentional.

So why am I approving? Because bots can still hop up on other sides of the railing, and maybe it's actually desirable at this position to keep the bots from jumping up. It actually kinda' makes sense for this part of the map.

Good point that this trend of deleting railing nav areas is not documented, but because NT players have such low jumping clearance, railings tend to be a source of OnStuck events which causes them to get cleared out by the nav editing harness. Generally bots with the current behavior code will waste their time trying to jump the railing, while shooting at the enemy, and doing badly at both. So generally as a trend you will notice the nav area connections around railings getting deleted in the other PRs as well.

As a general compromise, I would rather the bots waste time taking the long way around a railing and be too late, than the bots repeatedly failing to clear the railing and dying because their DPS towards the enemy is compromised by the jumping interruption against shooting

@sunzenshen
sunzenshen merged commit 2b26713 into NeotokyoRebuild:master Oct 14, 2026
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.

2 participants