Wolfenstein Modloader Buy me a coffeeKaffee spendieren

Wiki / Research reportsForschungsberichte

Mission Builder Kit: Custom Missions for the Gestapo Office Map (c2v1)

Partly solvedTeilweise gelöst Original page in the GitHub wikiOriginalseite im GitHub-Wiki

In plain wordsIn einfachen Worten

This report presents a mission builder for Wolfenstein II: The New Colossus. You describe objectives, triggers, enemy waves and the mission end in a small JSON file, and the tool writes the changed map entities, script nodes and a finished archive file. The demo has three objectives on the Gestapo Office DLC map: enter the offices, kill two waves of enemies, then escape. All 37 offline checks pass, but the demo has not been confirmed in the running game.

  • Objective texts must reuse existing retail objectives; a custom title like 'Eliminate the officers' is rejected on purpose.
  • The retail mission chain is switched off by removing 10 connections; the rest of the original script stays unchanged.
  • Every one of the 28 connection patterns used also appears in retail scripts.
  • Only two existing archive entries change and no new entries are added; 6 enemies and 2 spawners are newly added to the map, which has no in-game proof yet.
  • An offline simulation ran the demo mission from start to end, but it models the wiring, not the real game engine.

Dieser Bericht stellt einen Missions-Baukasten für Wolfenstein II: The New Colossus vor. Du beschreibst Ziele, Trigger, Gegnerwellen und das Missionsende in einer kleinen JSON-Datei, und das Werkzeug schreibt die geänderten Kartenobjekte, Skriptknoten und eine fertige Archivdatei. Die Demo hat drei Ziele auf der DLC-Karte Gestapo-Büro: in die Büros gelangen, zwei Gegnerwellen besiegen, dann fliehen. Alle 37 Offline-Prüfungen bestehen, im laufenden Spiel ist die Demo aber nicht bestätigt.

  • Zieltexte müssen vorhandene Ziele aus dem Spiel wiederverwenden; ein eigener Titel wie „Eliminiere die Offiziere“ wird absichtlich abgelehnt.
  • Die Original-Missionskette wird abgeschaltet, indem 10 Verbindungen entfernt werden; der Rest des Originalskripts bleibt unverändert.
  • Alle 28 verwendeten Verbindungsmuster kommen auch in Skripten des Spiels vor.
  • Nur zwei vorhandene Archiveinträge ändern sich, neue Einträge gibt es nicht; 6 Gegner und 2 Spawner kommen neu in die Karte, wofür es noch keinen Beweis im Spiel gibt.
  • Eine Offline-Simulation hat die Demo-Mission vom Start bis zum Ende durchlaufen, sie bildet aber nur die Verkabelung nach, nicht die echte Engine.

Technical detailsTechnische Details

Der technische Teil ist auf Englisch – so, wie er im Wiki steht. Unterstrichene Begriffe haben eine Erklärung auf Deutsch.

Status 2026-09-25. Tool tools/build_mission.py, demo in re_probes/missions/demo/. The game was not started; only read from <Steam library>.

python tools/build_mission.py re_probes/missions/demo/demo.json    # ~1:45 min, 37 probes, exit 1 on FAIL

Summary

Question Answer
What is built? A tool that translates a small JSON mission description into three things: Entity Edits, Kiscule nodes, and the finished container. The description includes objectives, triggers/locations, waves with Retail AI and spawn points, and the mission end.
Demo Three objectives in sequence: Enter the offices (ventilation shaft) → Kill all enemies (2 waves of 3 enemies each) → Escape (use exit) → Episode end. The file is re_probes/missions/demo/chunk_22.resources, sha256 5b660436cf9eb446b4acd7569c9a8e7a6b5b6d9a532394a20bffb1506d6cb398, 151 714 307 bytes.
Probes 37 OK, 0 FAIL (run.log). This includes probe_datahash 7127/7127 (1 entry skipped, as in the source), probe_idclhash +0x08 1/1 and +0x20 1/1, build_custom_map.check with 0 findings, Validator clean, every edge is a Retail edge pattern, 7 negative controls of the description, and 3 additional counter-tests.
Custom objective texts? No, honestly downgraded. Objectives are existing Retail missionobj with matching English titles (Track text: The language table loads before the DLC mount). A custom title like "Eliminate the officers" is rejected, this is a negative control.
In-game? Structurally valid, runtime unconfirmed. The steps for the test are in the Runbook (Section 6).

1. The Description (JSON)

name          [a-z0-9_]+  -> new Entities mission_<name>_…, Events wm_<name>_…
mission       mission-Decl (Retail, Start-Mount)           e.g. cobalt/c2v1/mission_c2v1_main
player_start  optional: spawn point, on which ALL idPlayerStart are set
spawn_points  {id: {"like": <Entity of the map>} | {"pos": [x, y, z]}}
places        {id: {"entity": <Entity of the map>, "at": <spawn point, optional = move>}}
waves         {id: [{"ai": <entityDef>, "at": <spawn point>}, …]}
groups        optional {Wave: Encounter-Group}           (aiEditable.groupName of the clones)
start         first objective
objectives    [{id, decl, title, complete: {enter: Location} | {use: Location} | {waves: [..], pause_ms},
                marker: Location (optional), then: <Objective> | "mission_end"}]

Rejected by (check_spec, every rule has a message):

  • unknown spawn points, locations, waves, and encounter groups
  • an AI definition that is not in the mount or for which there is no hidden, spawnable donor in the map
  • a missionobj missing from the start mount or whose English title does not match the specified title
  • a chain with a cycle, unknown target, or objectives outside the chain
  • a location not in the story layer layer/dlc/c2v1 or starting inactive (isActivatable = false)
  • moving an entity that uses another story script
  • a hidden use location without marker, because otherwise no one would show it

2. How it is built, each component with Retail proof

Objectives. The tool uses existing missionobj decls (Track text). The title is checked against english.lang during build.

Script. Our nodes are appended to the Retail mission script sacramento_gameplay_kiscule_c2v1.decl. Its binding is confirmed in-game: the screenshot shows "Enter the offices". The custom Retail mission chain is paused. To do this, all 10 impulse edges (20 edge ends) leading into the 8 Retail ActionMission nodes are removed at both ends.

Everything else in the script remains unchanged, including:

  • Sound dampener intro (CustomPlayerMode)
  • Tutorials and location titles
  • Autosaves
  • Hint markers

Verified that all 122 Retail nodes are identical to these edges except for the edges themselves, and no new edge is added. No other story script contains ActionMission.

Chain. It is structured like c2v1 (anatomy E01–E03):

  • Start: BeginGame → 500 ms → MissionStart (all objectives, idle) → 4100 ms → start_<Objective> → ObjectiveStart (HUD)
  • Transition: end_<Objective> → ObjectiveComplete → start_<next>
  • End: ObjectiveComplete + MissionComplete → end_level → 10 ms → ActionChronicle.End(completed) → ActionNextMap(MODE_AUTO)
  • Enter/Use: TriggerNotification/UseNotification (maxNotificationCount −1) → Gate (isOpen false, autoCloseCount 1, opened by objective start). The objective counts only while active.
  • Waves: ActionTrigger on a new idTarget_AISpawn → AiKilledNotification → Counter(N) → Delay → next wave or end_<Objective>
  • Markers: ActionWorldMarker(showHideEntity), Show at objective start, Hide at completion

Evidence. All 28 edge patterns and parameter values are present in Retail:

  • TriggerNotification with maxNotificationCount −1: 55 cases
  • UseNotification with −1: 358 cases
  • Gate with false/1: 83 cases
  • Received → ActionTrigger.Activate: 57 cases

The nodes are copies of Retail node templates and are created via probe_kiscule_write.Script (Track kiscule, only imported).

Enemies. For each enemy, the body of a hidden, spawnable Retail enemy from the same entityDef in this map is copied according to the same principle as build_custom_map.retype. Name and position come from the spawn point; the targets (scene points of the donor) are removed; 17 of the 44 Retail AIs in c2v1 also did not have them. The Encounter group is selectable.

Per wave, a new idTarget_AISpawn is created, copied from …spawn_firstarea. No new asset or Decl names are added; this has been verified.

Containers. Only two existing entries change: .entities (873) and the mission script (934). Both undergo rebuild and finalize. No new archive entries exist, and .mapresources remains unchanged.

3. The Demo (re_probes/missions/demo/demo.json)

Objective missionobj (Title) Completion
enter cobalt/c2v1/mission_c2v1_main_obj_01_find ("Enter the offices") Trigger sacramento_gameplay_triggers_obj_1_complete (Lobby shaft, Retail plaza)
survive cobalt/c1v1/mission_c1v1_main_obj_06_kill ("Kill all enemies") Wave 1: 3 × soldier_light at tactical_30/29/28 (y −168.9, Lobby north end). After the last kill + 3 s follows Wave 2: 2 × soldier_heavy at roam_24/25 and 1 × elite_heavy at tactical_27. All in group FirstArea.
exit cobalt/c2v1/mission_c2v1_main_obj_04_kill_all ("Escape") Use on sacramento_gameplay_prop_use_animated_exit_level with Marker. The exit is shifted from Hans' office (33,1 / −12,3 / 10,6) to the Retail hint marker "hint_lobby_exit" (0.97 / −166.05 / 1.5). It is an invisible 0.2-m CLIPMODEL_BOX Use point without baked geometry. → mission_end

Player start. Both idPlayerStart are located on the Retail story start in the lobby (−18.34 / −205.94 / −4.5). Consequently, the swap of Custom bundles for this demo has been reverted: Regardless of which start the engine takes, the player begins at the start of the chain. The rest of the custom map remains intact: 4 remodeled enemies, 10 pickups, 33 lights.

Offline replay. On the finished script ran an impulse loop (Sim: Delay immediate, Counter, Gate, Custom Events, Notifications). The result is exactly MissionStart(3) → Start/Complete per objective → MissionComplete → ActionChronicle.End → ActionNextMap.

Before each step, the loop fired all interfering events, and the mission never moved:

  • every trigger, every use, and every kill that any node listens to
  • every event sent by another story script

Two control tests show that these checks can be triggered:

  • If an enemy from the last wave remains alive, "Kill all enemies" stays open.
  • With gates that are open from the start, noise drives the mission.

4. Probes (37 OK, re_probes/missions/demo/run.log)

  • Mount:
    • All 3 AI-Defs exist under 996 entityDefs and have a provider in the map.
    • All 3 target Decls deliver their title from english.lang.
    • The only changed #str_ (#str_useable_fc_c2v3_escape) is present.
  • Description Negative Controls (7): Each is rejected:
    • Wave with a missing spawn point.
    • Target chain with a cycle (enter -> survive -> exit -> enter).
    • Unknown AI-Def.
    • Custom target text.
    • Moving a trigger used by an external entity.
    • Location only in the Challenge layer.
    • Hidden use location without a marker.
  • Entities:
    • Round-Trip bit-exact.
    • build_custom_map.check with 0 findings, no dangling reference.
    • Only the 3 claimed entities are changed; 8 new ones stand at the end, none is removed.
    • No new asset names.
  • Kiscule:
    • 48 nodes and 47 edges, each being a Retail pattern.
    • Validator clean, specifically alone, merged and read back from the container.
    • Text and compfile run bit-exact back.
    • The Retail part is identical except for the 20 edge ends.
    • Counter-probe: A dangling edge is rejected.
    • The event prefix is free.
  • Post-run: 5 Probes (Section 3).
  • Container:
    • Before finalize, exactly 2 data hashes are wrong, and probe_idclhash rejects.
    • After finalize, probe_idclhash and probe_datahash are green.
    • Only entries 873 and 934 differ from the source.
    • Reading back yields the same bytes.

5. Honest Compromises

  • No new target texts. The texts come from Retail ("Kill all enemies" instead of "Survive two waves"). There is no Retail title with "survive" or "wave" (Track text).
  • Retail mission content is dropped in c2v1. Retail targets 2–4 ("Locate Hans' office", "Kill Ubercommander Hans") no longer start. The Retail battles (firstarea, etc.) continue as before. Therefore, the 5 Lobby enemies of the Retail wave are added at startup. They do not count for "Kill all enemies".
  • The mission ends at the Lobby. The rest of the map remains unplayed because the exit has been moved for a short test. In demo.json, you can remove "at": "exit_spot", then the exit lies back in Hans' office.
  • The offline run is a model. Times are not modeled (Delay immediate), classFilter and engine orderings as well. The run proves the wiring, not the engine behavior.

6. Runbook (5 lines, PowerShell)

  1. Install: $d="<Steam library>\steamapps\common\Wolfenstein.II.The.New.Colossus\dlc\dlc_0\base\chunk_22.resources"; Remove-Item $d; Copy-Item "%USERPROFILE%\Documents\wolfenstein modloader\re_probes\missions\demo\chunk_22.resources" $d
  2. Start: Launch the game normally and restart the episode "Gestapo Office" (Agent Silent Death) fresh. Do not continue an old c2v1 save state, otherwise its mission progress will apply.
  3. What you see:
    1. You start in the red lobby.
    2. After approximately 5 seconds, "Enter the offices" appears.
    3. Squeeze through the ventilation shaft at the grate. The objective is complete, and "Kill all enemies" appears.
    4. At the north end of the lobby, 3 soldiers spawn. 3 seconds after the last one, 2 heavies and 1 elite spawn.
    5. Afterwards, "Escape" appears with a target marker in the lobby corridor leading toward the offices.
    6. There, use (hold for 1 second). The episode ends.
  4. How to recognize it: Objective 2 is named "Kill all enemies" (Retail: "Locate Hans' office"), and the exit is located in the lobby instead of Hans' office.
    • If "Enter the offices" is missing, the script does not load.
    • If no wave appears, the new enemy entities are not present.
    • If Objective 2 remains open, a wave enemy is still alive or stuck.
    • If markers and use are missing, the shifted exit does not trigger.
  5. Undo: Remove-Item "<Steam library>\steamapps\common\Wolfenstein.II.The.New.Colossus\dlc\dlc_0\base\chunk_22.resources"; Copy-Item "%USERPROFILE%\Documents\wolfenstein modloader\re_probes\dlc4_override\chunk_22.resources" "<Steam library>\steamapps\common\Wolfenstein.II.The.New.Colossus\dlc\dlc_0\base\chunk_22.resources". This is the file installed today, sha256 cadfbfce….

7. Risks and Open (Runtime)

  • New Entities. 6 KIs and 2 spawners are added to .entities. There is no runtime evidence in this project for this. Previous played edits only modified existing entities. The copies carry Retail bodies from the same map.
  • Retail portions without runtime evidence:
    • Objective completion, wave counting via AiKilledNotification, ActionChronicle/ActionNextMap(MODE_AUTO): They follow Retail patterns, but only the start of "Enter the offices" (anatomy) is proven at runtime.
    • An objective of a foreign mission (c1v1) under mission_c2v1_main: The principle is evidenced by E13 (parentMission c2v2), but this specific objective is untested.
  • Positions. The spawn points are Retail KI scene points on the lobby floor (z = 0), and the exit is a Retail hint marker. Whether navigation triggers there is not measurable offline.
    • If a wave enemy never dies, e.g., because it is unreachable or does not spawn, Objective 2 remains open.
  • Autosaves. Retail autosaves continue mid-mission. After reloading, the Kiscule state must come from the save state as in Retail.
  • Git. re_probes/missions/ is git-ignored via re_probes/*, including demo.json. Only tools/build_mission.py and this report are tracked.

8. Files

File Content
tools/build_mission.py Tool: Description check, Entity edits, Kiscule nodes, Merge, Replay, Container
re_probes/missions/demo/demo.json Description of the demo
re_probes/missions/demo/chunk_22.resources Finished file for dlc/dlc_0/base/
re_probes/missions/demo/mission_script.decl Merged mission script (plaintext)
re_probes/missions/demo/mission_nodes.graph.txt Our 48 nodes, readable
re_probes/missions/demo/manifest.json, run.log Hashes, edits, flow, negative controls, test run