Team Cereustooling wiki

Port an Elden Ring map into Nightreign

Partly checkedagainst the code on 2026-11-12 11:42

The bridge tool port_er_map copies the Asset parts of an Elden Ring map into loaded Nightreign maps in one go. It imports every model it needs into the project, then places the parts where they stood in Elden Ring, moved by an offset you give.

There is no button for this in the editor. It only exists as a bridge tool, so you run it through the MCP server (see mcp-bridge.md). To place single Elden Ring assets by hand, use the Object Palette: place-er-asset.md.

What was checked for this page: every parameter and default was read from the code. Nothing was run. The in-game notes at the end come from the team's notes on the sewers work, not from the code.

What it copies, and what it does not

Copied:

  • Asset parts of the Elden Ring map, and DummyAsset parts if you ask for them.
  • For each part: its model, position, rotation and scale.
  • Each distinct model, once, into the project: the same importer the Object Palette uses (model, collision, missing textures and materials). If Nightreign already uses that id for a different model, the import gets a free id of the same family, and the parts use that id. If Nightreign has its own copy of the same model, that copy is reused.

Not copied:

  • Map pieces, collision parts, enemies, regions, events, lights, navmesh, or anything else in the map.
  • Entity ids and entity groups. They are cleared by default (clear_ids).

How a part is made: it is a clone of a template Asset part of the target map, so its draw groups and flags are valid there. Then ModelName, Position, Rotation and Scale are set from the Elden Ring part. It is named <model>_<free number> (four digits) with a matching InstanceID.

Before you start

  • A Nightreign project with an Elden Ring install set up for it. Without one the tool answers "Porting ER maps needs a Nightreign project and an Elden Ring install." See project-setup.md.
  • The target Nightreign maps must be loaded, and each needs at least one Asset part to use as a template (and one DummyAsset part if you port that class).
  • If the target should be a new map, make it first with create_map_copy (below) and load it.
  • Check smithbox_status first. Do not run this in a Smithbox someone is editing without asking.

Steps

  1. Make the target map if it does not exist:

    create_map_copy(source="m60_44_36_00", new_id="m60_44_36_60")
    

    It writes map/mapstudio/<new_id>.msb.dcx into the project folder as a copy of the source's MSB, and lists the map in the Map Editor. Only the MSB is copied: no collision, navmesh, lights and so on. Map piece models resolve under the new id, so they may not draw. (with_navigation=True also copies the .nva and the navmesh binder; see generate-navmesh.md.)

  2. load_map every target map.

  3. Do a dry run. It places nothing:

    port_er_map(er_map="m35_00_00_00", target="m60_44_36_60", offset=[22, 90, 207], dry_run=True)
    

    It reports er_parts_read, matching_filters, outside_targets, to_place, per_target_map, distinct_models, the bounds and centroid of the matching parts in Elden Ring coordinates, and for the models: models_already_imported, models_same_as_game, models_need_import, models_missing_in_er and the first 40 of models_to_import.

  4. Run it with dry_run=False. The job runs inside Smithbox, a slice per frame, so the editor keeps drawing. By default the tool waits and polls port_status every 2 seconds until the state is done or error.

  5. Look at the result (screenshot, list_objects), then save_map each target map. The tool never saves.

port_er_map parameters

Parameter Default Meaning
target none One map id. Offset positions go straight into that map as its local positions.
tiles none A list of {"map_id": ..., "center": [x, z]} or [x, y, z]. See below. Give target or tiles, not both.
er_map "m35_00_00_00" The Elden Ring map to read.
offset [0, 0, 0] Added to each Elden Ring part position.
box none {"min": [x, y, z], "max": [x, y, z]} in Elden Ring coordinates, before the offset.
y_min, y_max none Height limits in Elden Ring coordinates, before the offset.
classes ["Asset"] "Asset" and/or "DummyAsset". Anything else is refused.
models none Only these Elden Ring model ids.
limit 0 (no limit) At most this many parts, first in MSB order.
dry_run False Report counts only.
name_models False Also give each imported model its Elden Ring community name.
clear_ids True Clear EntityID and EntityGroupIDs on the new parts.
wait True Poll until the job ends. False returns at once.
wait_timeout 3600 Seconds to wait before returning "still running".

Tiles. With tiles, each entry gives an m60 tile and its centre in the offset world. A part goes to the tile whose 256 m square [centre - 128, centre + 128) holds it, and its position is made tile-local by subtracting the centre. A [x, y, z] centre also subtracts y. Parts outside every tile are counted in outside_targets and not placed.

Positions are the part positions stored in the Elden Ring MSB plus offset.

port_status

No parameters. Returns the running or last job:

  • state: importing, placing, done, error or none
  • models_total, models_processed, models_imported_ok, models_failed, failed_models (with reasons), remapped_models (Elden Ring id to the id it got in the project)
  • parts_total, parts_placed, parts_skipped_no_model, and maps with planned, placed, skipped, done per target map
  • timings, error when it failed, and undo_steps when done

How to tell it worked

  • port_status says done, parts_placed equals parts_total minus the skipped ones, and failed_models is empty.
  • The parts show in the viewport with textures.
  • Each target map is one undo step. One undo per map takes the port out again. The imported model files stay in the project folder.

What goes wrong

Message Cause
"Give a target map id, or tiles [{map_id, center}]." Neither target nor tiles was given.
"Give either target or tiles, not both." Both were given.
"No parts match (see the dry_run counts)." The filters or the tile squares leave nothing. Check offset.
"Map ... is not loaded. Call load_map first." A target map is not loaded. This is checked before anything is copied.
"... has no Asset part to use as a template." The target map has no part of that class. Leave one in.
"A port is still running (see port_status)." Wait for the running job.
"Elden Ring has no /map/mapstudio/....msb.dcx." Wrong er_map id.
Parts are skipped Their model failed to import. failed_models in port_status says why.

Limits

  • Assets only. A ported area has no floor collision of its own beyond what the assets carry, no navmesh and no lights.
  • The Elden Ring MSB is read once and kept in memory for the next call with the same er_map.
  • A bulk port can import hundreds of models. Imports are copied into the project folder at once, before any save.

In game

Not proven as a whole. From the team's notes on the sewers work (local tests, 2026-11-11), not re-checked here:

  • Ported Elden Ring assets on their own ids did draw in game.
  • Static assets' collision ignored the MSB scale, so pieces placed at a scale other than 1 had collision of the wrong size until their collision files were rebuilt at that scale.
  • Collision inside the ported rooms and textures were still unverified.

Treat anything else as untested until someone writes the result here.

Source files this page documents (4)
  • smithbox:src/Smithbox.Program/Utilities/McpBridge/ErMapPort.cs
  • smithbox:src/Smithbox.Program/Utilities/McpBridge/McpTools.cs
  • smithbox:src/Smithbox.Program/Editors/Map Editor/Tools/Object Palette/ErAssetImporter.cs
  • smithbox:Tools/mcp/smithbox_mcp.py

Changed one of these? Update this page: smithbox/port-er-map.md (how).