Skip to content

ntre_ballistrade_ctg: navmesh rework - #138

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

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

Conversation

@sunzenshen

@sunzenshen sunzenshen commented Sep 26, 2026 •

Copy link
Copy Markdown
Contributor

ntre_ballistrade_ctg: navmesh rework

One file, neo/maps/nav/ntre_ballistrade_ctg.nav.

Dependencies and status

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

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

Pending before review:

  • Nothing: ready for review.

At a glance

master this PR
nav areas 1310 928
file size 1,192,466 bytes 574,042 bytes (-52 %)
areas no spawn or objective can reach 19 (14 pockets) 0
areas the engine blocks at every round start 11 0
bot stuck events a match (commits 1-2, see below) 33.1 7.2

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 (added 2026-10-02) - walk links that ran one way only, found by an outside review, and climbs that had lost their way back down.
  4. Areas inside props, bollards and a doorway (added 2026-10-02) - found with the engine hull audit run for commit 3.

1. What changed

  • Areas the engine blocks at round start. CNavArea::UpdateBlocked finds a solid (mostly a prop) at the center of 11 of master's areas on every restart. The path cost then refuses them for the whole round, so bots plan routes they cannot take. They are cut around the obstacle or dropped.
  • Props and thin obstacles:
    • standing room is carved around props
    • a walk link a player hull cannot cross, past a signage stand, is cut (in one direction; commit 3 restores it)
  • Hand edits where test bots got stuck:
    • the stair handrail over the one-way drop near the stairs
    • a wall block
    • the doorway beside the window (marked PRECISE, so bots walk through it rather than cutting the corner)
    • a motorcycle
  • Raised tops. The mesh had 16 areas on raised tops with drops out and no way up. They were compared against the class crouch jumps (Recon 72 u, Assault 53 u, Support 47 u):
    • four tops at 46-54 u get a one-way climb link from the floor next to them (commit 4 removes one, which has no standing room)
    • a fifth at 64 u was linked too, but no bot got within 114 u of it in 6 pinned-ghost runs, so it is removed
    • the rest, at 67-118 u, are beyond what bots can climb, and are removed
  • 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

928 of the 933 areas carry the id of the master area they match. Kept ids that sit more than 64 u from their master location drop from 68 to 12, and an id-keyed diff shows 5 added / 382 removed areas. Geometry, connections, attributes and the analysis data are unchanged by this step.

3-4. Review fixes

An outside review of this PR found eight walk links between areas at the same height that ran one way only: an earlier pass that cut links a player hull cannot cross judged each direction on its own. Each pair was swept in both directions with the engine's hull audit (a player hull moved along the link in the server, recording the props and brushes it hits). Commit 3:

  • Five pairs blocked both ways are cut both ways: four through the clothing racks near (1900, -750) and one through the flatbed trailer near (-150, -2170).
  • Two pairs clear on every lane by the clothing racks get their missing direction back.
  • The link past the signage stands near (1188, -1112) gets its missing direction back too, against the audit: the stands are physics props, and this series leaves props that move to the bots at run time. The stands' other links already ran both ways.

Also in commit 3, from the structural check run for this review (not in the review): four climbs of 34-48 u had lost their way back down, onto wall tops near (772, -40), (1302, -2095) and (1940, -1132) and a crouch nook near (488, 962). A drop is always possible where a climb is, so the drops are restored, as on master. A fifth such wall-top strip near (548, -1105), 5 u wide and inside the gutter wall, has no room for a player hull and is deleted.

Beyond the review (commit 4). The engine hull audit of commit 3's mesh (a player hull also stood every 4 u on every area) lists links blocked on every lane in both directions and areas no hull fits in. Each was checked against whether bots crossed it or stood in it in the test matches below, and the stuck events near it.

  • Five areas no hull fits in, none of which a bot stood in during 56 matches, are deleted (3 hiding spots): beside the clothing racks near (1966, -738) and (1966, -788), beside the paper screen near (1788, -616), on the edge of the blockade near (-338, 641), and the top of the winged car near (1912, -2238), whose wing sits 4 u over its roof. That car top was one of section 1's four linked tops; the probes there were picked up from the floor beside the car.
  • Twelve links across the bollards at x 0 and x 412 are cut both ways. Bots stand beside the posts and walk round them; they crossed these links at most once in 56 matches.
  • The doorway near (462, -2830) between the room at z 100 and the street had no area in it: the link spanned the 25 u wall, and bots leaving the room skipped past the opening and pressed the wall stub north of it. It was the map's largest stuck spot in the test matches (105 and 89 events in 6 of 28 matches on commits 1-2 and 3). A 25 x 28 u area in the opening, marked PRECISE like the doorway 4109 in section 1, takes the link's place: 2 events in 1 of 28 matches.

Kept: 11 pairs blocked by props that move during a round (the motorcycle near (800, -2600), which bots crossed 148 times in 56 matches; office chairs, recycle bins, the signage stands, a cardboard box) or by a breakable window near (1938, -862) (45 crossings). Those are left to the bots at run time.

Existing areas keep their ids; the doorway is a new area. Re-analyzed in game after each commit.

How it was tested

  • Reachability:

    • every spawn can reach every other spawn
    • every objective is reachable from a spawn
    • every area is reachable from a spawn or an objective, also counting no walk link that climbs 68 u or more (the most a Recon bot plans to jump; master 19 areas)
    • every walk link between two areas at the same height runs both ways, and no climb leads into an area without a way back: no dead end and no trap for any class
    • the engine hull audit of this mesh: of 1536 pairs of areas at the same height, none is blocked both ways by a wall or a static prop; 11 are blocked by props that move or a breakable window (commit 4)
    • no area is blocked at round start: offline, and in the engine in a test match (master: 11)
    • the mesh is one connected piece
  • Master against this mesh, same bot code, one batch. Byte-identical copies of the map, one carrying each .nav. CTG with the ghost pinned at each of the 7 ghost spawns, 3 matches each, 30 bots, default weapon damage. The count excludes stuck events in the spawn crush at round start and the weapon-scavenging behavior.

    • stuck events 33.1 -> 7.2 a match (paired x0.21, 95 % interval [0.13, 0.34], 18 of 21 pairs down)
    • FAIL_NO_PATH_EXISTS move failures 7.2 -> 5.4 a match
    • "fell off the path" failures 0.3 -> 0
    • kills 44.9 -> 42.9 a match
    • no fall deaths in either
  • Bots still see and shoot across the mesh. 89 % of kills are between bots in different nav areas and 73 % in non-adjacent ones, against 93 % / 76 % on master, at a similar kill distance (median 278 u against 283 u).

  • Commits 3-4 against commits 1-2 (2026-10-02), on upstream master + #2176 + #2186 with logging, the same design (each of the 7 ghost spawns pinned, 30 bots, default weapon damage, 8 matches at a time). Every match reached match end with 0 asserts and no crash (140 matches).

    • Commits 1-4 against commits 1-2, 28 pairs: stuck events 10.4 -> 5.7 a match (paired x0.75, 95 % interval [0.38, 1.48], 16 pairs down, 11 up)
    • Commit 3 against commits 1-2, 56 pairs over two batches: 11.7 -> 9.0 a match (x0.88 [0.61, 1.28], 27 down / 26 up); within 64 u of its changed links 20 -> 41 stuck events, mostly at the clothing rack corner near (1950, -788) on both meshes (18 -> 31) and 8 in one match at the signage stands
    • Commit 4 against commit 3, 28 pairs: x0.61 [0.34, 1.09] (16 down, 10 up); at commit 3's changed links 18 -> 0 (two of the clothing rack areas go in commit 4); at the doorway 89 -> 2
    • FAIL_NO_PATH_EXISTS 5.7 -> 7.0 a match; kills 40.7 -> 40.9; ghost captures 35 -> 36 of 56 rounds; no fall deaths; 91 % of kills between bots in different nav areas (92 % before)

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: a link can cross a gap the player hull cannot fit through, or lead along a route path planning never picks. To test one area in game, the area is made the objective and bots are sent to get it.

  • The ghost is placed on the area 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". It replaces the position the round's chosen ghost spawn would have used, so any point becomes the CTG objective. The ghost is dropped a little above the area and settles onto it as it would from a real spawn. The map file is not edited.
  • A pickup at the spot means bots can reach the area. Bots run their normal CTG errand to the ghost. A pickup is the clear signal; a bot coming within 160 u is a weaker one. The position of the first pickup is checked, because a ghost on a low raised top can be taken from the floor below, and a ghost on a narrow ledge can fall off it.
  • A miss only counts with a control. One run is a screen. A spot bots miss is re-run 6 times beside a control spot on the same map that bots are known to reach, in the same batch. Without the control, "not reachable" and "the probe did not work" look the same.
  • Weapon damage is turned down for these probes, so bots move instead of dying in fights on the way.
  • Master's mesh can run beside this one. The game loads a .nav by map name, so the harness copies the .bsp byte for byte under a second name (for example ntre_oilstainb_ctg) that loads its own .nav. The same pin then runs on both meshes in one batch. The copy only carries the second mesh; the ghost comes from the cvar.

On this map. The five raised tops linked in this PR were probed on a byte-identical copy, 6 runs each, with a control picked up 6/6. Four were picked up 6/6, mostly from the floor below, so the probe says little about the climb itself; the fifth was 0/6 and is removed. Earlier screens on older revisions got pickups at the largest floor region removed from master's mesh (4/4) and at four areas no bot had visited.

One file. Against master: 1310 -> 933 areas, 1,192,466 -> 572,829
bytes; 19 areas no spawn or objective reaches (14 pockets) -> 0; 11
areas the engine blocks at every round start -> 0.

- Areas CNavArea::UpdateBlocked finds solid at their center at round
  start are cut around the obstacle or dropped.
- Standing room is carved around props, and a walk link a player hull
  cannot cross (past a signage stand) is cut.
- Hand edits where test bots got stuck: the stair handrail over the
  one-way drop by 5076, a wall block, the doorway beside the window
  (PRECISE) and a motorcycle.
- Raised tops a player can climb (46-54 u) get a one-way climb link;
  tops no bot can reach are removed.
- Re-analyzed in game after the edits.
Renumbers area ids only, so an id-keyed diff against master reads
sensibly: 928 of the 933 areas carry the id of the master area they
match, kept ids that moved more than 64 u from their master location
drop from 68 to 12, and the diff shows 5 added / 382 removed areas.
Geometry, connections, attributes and the analyzed visibility data are
unchanged (verified topology-identical).

@sunzenshen sunzenshen left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

At an overview, most of the changes involved merging adjacent areas to form bigger ones to reduce the number of nodes bloating the nav file size and A* traversal space.

Before:

Image

(Red means the area was deleted in the edits.)

After:

Image

Doesn't seem to be any red flags in terms of deleted areas that shouldn't have been.

@sunzenshen
sunzenshen marked this pull request as ready for review September 28, 2026 07:30
@sunzenshen

Copy link
Copy Markdown
Contributor Author

Their traversal is a bit inconsistent, but you can order bots to climb and wait on the incline of the street tunnel wall, so I'll leave those NavAreas in:

image

An outside review of this mesh found walk links that run one way
only. Each pair was swept with a player hull in both directions by
the engine's hull audit:

- Cut both ways the five pairs blocked both ways (clothing racks, the
  flatbed trailer); restore the missing direction of the two pairs
  clear both ways.
- Restore 3440 -> 3437 past the signage stands near (1188, -1112).
  They are physics props, which bots meet as they stand at run time,
  so the link is kept both ways like the stands' other links.
- Four climbs of 34-48 u onto wall tops and a crouch nook had lost
  their drop back; restore the drop. Delete 5215, a 5 u strip inside
  the gutter wall near (548, -1105) that no player hull fits on.

Existing areas keep their ids. Re-analyzed in game (same graph,
fresh visibility).
The engine hull audit run for the review fixes also lists level
links blocked on every lane in both directions. Where the blocker is
static and bots do not cross:

- Delete five areas no player hull fits in: beside the clothing
  racks near (1966, -760), beside the paper screen near (1788, -616),
  on the blockade's edge near (-338, 641), and on the winged car near
  (1912, -2238), whose wing sits 4 u over its roof.
- Cut twelve links across the bollards at x 0 and x 412, which bots
  walk round and never cross in 56 test matches.

Add a 25 x 28 u area in the doorway at (462, -2830) and mark it
PRECISE. The link it replaces spanned the wall, and bots leaving the
room cut the corner into the wall beside the door: the map's largest
stuck spot in the test matches.

Links blocked by movable props are kept. Existing areas keep their
ids. Re-analyzed in game (same graph, fresh visibility).

@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.

Ballistrade had no obvious issues. Approving.

@sunzenshen
sunzenshen merged commit dc76c88 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