Smithbox can build the navmesh of a Nightreign (or Elden Ring) map from the collision of the parts placed in it.
There are two routes: the Navigation Editor in the Map Editor, and the MCP bridge tool nav_generate.
Not proven in game. The generated files re-read, pass Smithbox's own checks and compare well with the game's meshes, but nobody has yet watched an enemy path on one. Read Known limits first.
If you only need enemies to walk around props you added, you do not need this page. Flag the props instead. That is proven in game. See The proven alternative.
What was checked for this page: every name, parameter and default below was read from the code. Nothing was run. The in-game statements come from the team's notes, not from the code.
What a map's navigation is
- One
.nvafile per map. It lists the map's navmesh models. - One
.nvmhktbndbinder per map. It holds two Havok files per model: annfile (normal characters) and anofile (large characters), named<n|o><map digits>_<model>.hkx. - One navmesh serves every variation of a map. There is no variation field in the files.
Before you start
- A Nightreign or Elden Ring project. The Navigation Editor refuses other games.
- The map must have an
.nvaand a navmesh binder already. Generation needs an existing navmesh file of the map as a template. A brand new map id has none, so start from a copy (step 1 of the MCP route). - Havok loading must be on (the Map Editor setting
MapEditor_SkipHavokLoadmust be off), or the navmesh files cannot be read. Collision loading must be on for the in-editor route. - Work on a copy, not on a vanilla camp. A camp's navmesh is shared by all its variations.
Route 1: in the editor
- Load the map in the Map Editor.
- In the Tools window, open the Navigation Editor section. If it is missing, turn it on in the Tools window's View menu: Navigation.
- Under Map, pick the loaded map.
- Open the Models tab and scroll to the Generate group.
- In the viewport, select the collision parts and assets to walk on. The group shows them under Selected parts ("N collision parts and M assets selected"). Dummy assets are not used.
- Tick Blocker on a selected part to generate around it instead of on it. Blocker tags last for the session. Every tagged part of a loaded map counts in each generation, selected or not. The Blockers list has Untag per part and Clear. Tagged parts show as red boxes while the overlay is on.
- Pick the target:
- As a new model
- Replace model NNNNNN's navmesh (the model selected in the Models list)
- Into a free placeholder, with the option Give it the collision model's ID
- Adjust n set and o set if needed. Each has: Cell size, Agent radius, Agent height, Max climb, Max slope, Max edge length, Face size, Triangles only, Jump and fall links (then Jump height, Fall height, Link reach), and Reset.
- Press Generate. It runs in the background ("Generating..."). The result is applied as one undo step.
- Save the map (File > Save). The
.nvaand the navmesh binder go into the project folder.
The editor route has no "solid" role, no variation filter and no MinIslandArea setting. Use the MCP route for those.
Route 2: through the MCP bridge
Read mcp-bridge.md first, above all its safety rules. Check smithbox_status before you touch a
running Smithbox.
-
Make a copy of the camp with its navigation:
create_map_copy(source="m43_02_00_00", new_id="m43_02_00_90", with_navigation=True)This writes the MSB under the new id into the project folder. With
with_navigationit also copies the source's.nvaand.nvmhktbnd, with the files inside the binder renamed after the new map. Nothing else is copied (no collision, lights and so on).new_idmust look likem43_02_00_90and must not exist yet. -
load_map(map_id="m43_02_00_90") -
nav_info(map_id=...)lists the map's navmesh models:model_id,n_name_id,o_name_id,n_faces,o_faces,placeholder,n_clearance_seeded, plusnva_layoutand the binder'sfiles. -
Try it without changing anything:
nav_generate(map_id="m43_02_00_90", variation=2, n={"MinIslandArea": 20}, o={"MinIslandArea": 20}, blockers=["AEG099_123"], dry_run=True) -
Run it for real with
dry_run=False. Addsave=Trueto save the map straight away, or callsave_maplater. -
nav_validate(map_id=...)runs the editor's validator on the result. It returnserrors,warnings,by_code, the same counts for the game's own files (vanilla_by_code) andcodes_not_in_vanilla. Anything incodes_not_in_vanillais new with your edit.
nav_generate parameters
| Parameter | Default | Meaning |
|---|---|---|
map_id |
required | A loaded map. |
walkable |
none | Regexes on part names or models. Matching parts are walkable. |
blockers |
none | Regexes. Matching parts are solid and their collision box is cut out of the mesh. |
solids |
none | Regexes. Matching parts are solid and never walkable. No box is cut. |
default_role |
"walkable" |
Role of parts no regex matches: "walkable" or "none". |
assets |
True |
Asset parts may take the default role. Only applies while walkable is empty. |
collision |
True |
Map collision parts may take the default role. Only applies while walkable is empty. |
variation |
not set | Keep only parts of this NR variation plus the shared ones (-1). |
n, o |
none | Overrides of the generation settings for each set, by name. See below. |
seed_clearance |
True |
Write clearance caches laid out like the template file's. |
target |
"replace" |
"replace", "slot" (a free placeholder pair) or "new". |
model_id |
-1 |
For replace: the model to replace. May be left out when the map has exactly one replaceable model. For slot: the id the slot is renamed to. For new: the new id (else the next free id from 900000). |
dry_run |
False |
Generate and report only. |
save |
False |
Save the map after the edit. |
Regexes ignore case and match a part's name or its model. A part that matches several lists is a blocker first, then a solid, then walkable.
Only Asset and Collision parts with a model are read. Map collision comes from the map's high collision binder.
An asset's collision comes from its own collision binder (_h, else _l).
Settings you can override in n and o (an unknown name is refused with the list of known names):
| Name | n default | o default |
|---|---|---|
CellSize |
0.2 | 0.3 |
CellHeight |
0.1 | 0.1 |
MaxSlope |
60 | 60 |
AgentHeight |
1.8 | 2.5 |
AgentRadius |
0.1 | 0.3 |
MaxClimb |
0.8 | 1.2 |
MinRegionArea |
2 | 6 |
MergeRegionArea |
20 | 20 |
MaxEdgeLength |
8 | 12 |
MaxEdgeError |
1.0 | 1.0 |
MaxVerticesPerFace |
6 | 6 |
SnapToCollision |
true | true |
FaceSize |
2 | 2 |
TrianglesOnly |
false | false |
Links |
false | false |
JumpHeight |
2.5 | 0 |
FallHeight |
15 | 15 |
LinkReach |
5 | 9 |
MinIslandArea |
0 | 0 |
MinIslandArea drops joined pieces of the finished mesh smaller than that many square metres (prop tops, roofs,
ledges). The comparison runs in the findings document used 20.
What comes back
Part counts per role, missing_collision (models with no collision found), walkable_triangles,
solid_triangles, blocker_boxes, seconds, and for each set faces, edges, vertices, islands_dropped,
faces_clipped_by_blockers, jump_links, fall_links. After a real run: applied, the binder files changed,
validator_errors, validator_warnings, and saved_to when save was set.
How to tell it worked
nav_generatereturnsapplied: trueand face counts above zero fornando.nav_validateshows nothing incodes_not_in_vanilla.- In the editor, the Navigation Editor's Overlay tab draws the new mesh over the map.
- Whether enemies use it in game is not proven. Test that yourself and write the result here.
What goes wrong
| Message | Cause |
|---|---|
| "... has no NVA loaded" | The map has no .nva, or Havok loading was off when it loaded. |
| "...'s navmesh files aren't readable" | Havok loading is off. Turn it on and load the map again. |
| "No walkable collision: ..." | No part with a walkable role had collision. The message lists the models with none. |
| "No model to replace: give model_id, one of ..." | The map has more than one replaceable model. Pass model_id. |
| "The map has no free placeholder pair." | target="slot" on a map with no free placeholder. |
| "The map has no navmesh file with a query mediator to build from." | No template. Copy a map that has a navmesh. |
Known limits
From Documentation/Claude/Navmesh Generation Plan.md, section 6 (2026-11-11):
- Not tested in game. Whether enemies path on a generated mesh is open.
- A copied map keeps the source's NameIDs and map UID. A new map loaded beside its source may clash.
- No jump or fall links by default (
Linksis off). Most of the game'snfiles have them. - No gate nodes.
- No face data flags. The game sets
0x1000000on about 6% ofnfaces; generated meshes leave it unset. - m60 open-world tiles also need NVC connector files. Smithbox does not make them.
- Coverage against the game's own meshes, with assets and collision as input and
MinIslandArea20: the camp m43_02_00_00 covered 87% of the game'snarea and 80% of itsoarea; the church m41_01_00_00 90% and 86%. Generated meshes have more faces than the game's because of the 2 m face grid. - A POI map's own collision is a two-triangle placeholder. The ground there comes from assets, so leave
assetson.
The proven alternative: flag the blocking props
Each asset has a row in the param AssetEnvironmentGeometryParam. Its field navimeshFlag ("Pre-destruction
Navmesh Flag") tells the game how the asset affects pathing. The first values of its enum ASSET_NAVIMESH_FLAG are
0 None, 1 Obstacle, 2 Impassable.
Setting this flag on the rows of blocking props was tested in game on the Boneyard variation of camp m43_02_00_00 (2026-11-11): enemies pathed around the props much better, with no navmesh edit. This is the team's note of that test; this page's writer did not repeat it.
So for a custom POI or props added to a camp:
- Find the asset rows of the props that block the way.
- Set
navimeshFlagto Obstacle or Impassable on them, as a param edit in your stage. - Leave the navmesh alone.
Only generate a navmesh when the ground itself is new (a new map id with new floors).
To inspect or patch an existing navmesh file without Smithbox, the Havok2014 tool has navinfo, navcover,
navunlink and navclearance. They are described in ../ports/map-tools.md.