Wolfenstein Modloader Buy me a coffeeKaffee spendieren

Wiki / Latest research roundsNeueste Forschungsrunden

Fixing the Missing HUD Objective Banner for Out-of-Order Missions

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

In plain wordsIn einfachen Worten

In a custom Wolfenstein II: The New Colossus mission, the HUD objective banner ('Enter the offices') did not appear when the mission was started out of story order, for example via chapter select. The research found this is a deliberate rule (only one main-story objective is tracked at a time), not a bug, and a shipped developer test script solves the same case by also calling SetActiveObjective. The resulting fix to the project's mission-building tool passes the tool's full offline check pipeline, but has not been confirmed in the running game.

  • The gate reads a copy of the mission's TYPE_MAIN flag; no code anywhere in the 92 MB program writes the field it checks.
  • Only 4 of 2,408 retail mission scripts use SetActiveObjective; one developer test script does exactly this fix.
  • The fix adds a SetActiveObjective call with showNotification='false' right after ObjectiveStart.
  • 'false' is chosen because 'true' risks a double banner; the flag is only cosmetic.
  • In-game test: rebuild the mission, start the episode fresh and check whether the banner appears.

In einer eigenen Mission für Wolfenstein II: The New Colossus erschien das HUD-Zielbanner („Enter the offices“) nicht, wenn die Mission außerhalb der Story-Reihenfolge gestartet wurde, etwa über die Kapitelauswahl. Die Forschung zeigt: Das ist eine bewusste Regel (es wird immer nur ein Hauptziel verfolgt), kein Fehler, und ein mitgeliefertes Entwickler-Testskript löst genau diesen Fall, indem es zusätzlich SetActiveObjective aufruft. Der daraus abgeleitete Fix für das Missionswerkzeug des Projekts besteht alle Offline-Prüfungen des Werkzeugs, ist aber im laufenden Spiel noch nicht bestätigt.

  • Die Sperre liest eine Kopie des TYPE_MAIN-Flags der Mission; nirgends im 92-MB-Programm wird das geprüfte Feld geschrieben.
  • Nur 4 von 2.408 Original-Missionsskripten nutzen SetActiveObjective; ein Entwickler-Testskript macht genau diesen Fix.
  • Der Fix ergänzt direkt nach ObjectiveStart einen Aufruf von SetActiveObjective mit showNotification=„false“.
  • „false“ wurde gewählt, weil „true“ ein doppeltes Banner riskiert; das Flag ist nur kosmetisch.
  • Test im Spiel: Mission neu bauen, Episode frisch starten und prüfen, ob das Banner erscheint.

Technical detailsTechnische Details

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

Earlier work found that a custom map's HUD objective banner ("Enter the offices") doesn't appear when a mission is entered out of normal story progression — for example, jumping straight to it via chapter select — and traced the gate to a field read at a fixed memory offset on the player object whose writer couldn't be found anywhere in the executable. This pass pushed the search further across the entire 92 MB executable image (not just the code section) using four additional search methods, and still found no writer — but worked out what the gate actually checks, and found a shipped developer test script that solves exactly this scenario the same way this report's fix does. The result is a confirmed three-line code change to the project's mission-building tool, validated by running the tool's own full build-and-check pipeline end to end; the fix itself has not yet been confirmed inside a running game.

What the gate actually is

The suspicious field is not a per-session write at all — it's a copy of the mission decl's own missionFlags field. The game's internal enum table shows TYPE_MAIN is exactly the bit the gate tests, and every main-story mission decl checked (including the one the custom demo map uses) is TYPE_MAIN. So the real rule is: "if the player is still tracking a different main-story mission, don't show this one's banner" — a deliberate "one main-story objective tracked at a time" design, not a bug. Nothing in the normal ObjectiveStart/MissionStart mission-script handlers, nor the custom chain's own nodes, ever writes the tracked-mission pointer that this gate reads — confirmed by an exhaustive, five-method search of the entire executable image (direct displacement, a data-driven offset table, 16/64-bit and big-endian encodings, and a compiler "split-offset" pattern) that still finds the literal field offset exactly once: the read, never a write.

The confirmed fix

A corpus-wide scan of all 2,408 retail mission scripts' use of the SetActiveObjective node found only four wired uses in the entire game. One of them is a shipped developer test harness script that does exactly what this fix proposes: fires ObjectiveStart immediately followed (500 ms later) by SetActiveObjective, from the same out-of-band trigger, with showNotification="false" on the SetActiveObjective call. This is strong precedent that the game's own developers solved this exact "entered outside normal progression" scenario the same way.

SetActiveObjective's own notifier function (traced in full this pass) has a different, same-mission TYPE_MAIN check that is always satisfied here, and its showNotification parameter only selects a cosmetic "banner kind" flag on the queued HUD record — it does not gate whether the state update happens at all. That's why "false" (not "true") is the correct value: using "true" risks a double banner flash in the normal, non-broken case.

The exact change, tools/build_mission.py

         get(oid + ".start", "start_" + oid, 0)
         mission(oid + ".show", [o["decl"]], "true", 1)
         s.link(oid + ".start", "Received", oid + ".show", "ObjectiveStart")
+        mission(oid + ".activate", [o["decl"]], "false", 2)
+        s.link(oid + ".start", "Received", oid + ".activate", "SetActiveObjective")
         get(oid + ".end", "end_" + oid, 8)

Two more small edits are needed only because the tool's own offline self-check hardcodes the expected event trace — not because anything is functionally wrong without them:

     for i in chain:
-        exp += [("ObjectiveStart", (by[i]["decl"],)), ("ObjectiveComplete", (by[i]["decl"],))]
+        exp += [("ObjectiveStart", (by[i]["decl"],)), ("SetActiveObjective", (by[i]["decl"],)),
+                ("ObjectiveComplete", (by[i]["decl"],))]
-        cut_at = exp.index(("ObjectiveStart", (wobj["decl"],))) + 1
+        cut_at = exp.index(("SetActiveObjective", (wobj["decl"],))) + 1

This exact diff was run through the whole build_mission.py pipeline end to end (structure validation, retail-edge-pattern checks, offline event simulation including negative controls, container hashing) and passes every check; the unmodified baseline run reproduces the exact container hash already on record, confirming no incidental drift from the harness itself.

In-game test

  1. Apply the three edits above and rebuild the mission container (python tools/build_mission.py <demo spec>).
  2. Install as before (copy into the custom-map DLC folder) and start the "Gestapo Office" episode fresh (not a continued save).
  3. If "Enter the offices" now appears: fix confirmed. If not: enable the mission debug cvar, play to the same point, and check the console dump for a "Set active on missionIndex" log line — its absence points to a node-wiring problem before this path; its presence with still no visible banner points to a problem inside the banner-drawing code itself, beyond anything this fix touches.