Skip to content

Research Report Explosivpistole Diag

LopeKinz edited this page Oct 1, 2026 · 2 revisions

Explosive Pistol Does Not Explode: Diagnosis

Status as of 2026-09-26. All information is read-only: Live archives under D:/.../Wolfenstein.II.The.New.Colossus, runtime snapshot re_probes/runtime_87400/module.bin (disassembled with tools/tnc_runtime_code.py). Nothing written under <Steam library> or Saved Games, no project file other than this report.

User Finding: The magazine shows 30 rounds. Therefore, the Decl route from tncpatch reaches the game (ammo clipSize or Upgrade valueInt, both set to 30). The pistol still does not explode. Tested on the Kit map (dlc_0, c2v1).

Result

Cause 1 (High Probability): The loading order breaks the new inheritance. projectile/player/pistol appears in the preload list before its new parent projectile/player/kampf_pistol_rocket. The engine places all entries "deferred" first and then loads them sequentially. When the pistol is parsed, inherit finds the rocket object already created but not yet loaded. The loader does not load it within this nesting. The parser copies the empty parent without warning. The pistol is thus "Class standard values + own edit block", remaining a hitscan weapon without splashDamageDecl. The ammunition change takes effect because it resides in its own edit block and its parent base_ammo is loaded beforehand.

Evidence Result Could have turned out differently
Order of inherit parent vs. child in common.mapresources (patch_4, the read copy), types projectile/ammo/weapon/damage/upgrade 298 parents before, 1 after: exactly our pistol (line 3221, rocket 8769) yes, retail exceptions would have proven "order irrelevant"
same in c2v1.mapresources (dlc_0), 17 Decl types including entityDef 844 before, 26 in common (loaded earlier there), 1 after: our pistol (8016 vs. 13217) yes
All 129 manifests: which line loads first? every manifest with both lines has the pistol first (5 common copies + DLC-c2 maps); no campaign map lists either yes
Block loader LoadNamedResourceBlock 0xAC1730 Phase 1 0xAC2FE0..0xAC306C: each line is created (vtable+0xC8) and marked with `flags =4(deferred) ("allocate and defer load"). Phase 20xAC3290..0xAC339B: Load(name,0,0)` in manifest order
Search/Load 0xAB8F80 deferred ([+0x4C]&4): level 0 loads. Nested ([0x30B3EF4]!=0) loads only when [+0x40]==2 (static resource), otherwise returns unloaded (0xAB9224..0xAB9236) yes
idResourceList::Load 0xABA110 found object with [+0x18]==0 is returned directly (0xABA271..0xABA29D), without load check yes
Decl Parser 0xA55000 (Slot 26 of vtable for idDeclProjectile and idDeclAmmo, checked via RTTI) inherit → Load(parent, mode 1) (0xA5519C). If parent is "in progress" (&2), it generates FatalError "…used for parent inheritance … before it is fully defined". The parser does not check if it is loaded, it copies it (0xA55416..0xA55460) yes, a load check or reload would have refuted the cause
Is the rocket a static engine resource ([+0x40]=2)? no: The static list at 0x30B4058 has 1289 nodes, none is the rocket, and the string appears nowhere in the Exe. Constructor 0xAB3820 sets 0 or 1. 0xABBAE0 (all static) runs only after game initialization (0xA66A29) yes
Subsequent reload if parent loads later? registration 0xA410A0 returns immediately when [0x30B54E8]==2. The block loader sets this before Phase 2 (0xAC31D8), live value is 2. Registry 0x3C789F0 otherwise uses only reloadDecls (0xA415E0, not exposed) yes

Proven are the code path and the data state. It has been established that the path is traversed exactly as described at runtime and that an unloaded Decl object carries only class default values. Side effect, if this holds: The pistol also loses everything from projectile/player/default (recoil weaponRecoilInfo, tracerMtr, spread).

Also applies to campaign maps: Init2 (0xABD340) loads init at startup and then common (0xABD442, 0xABD520). Pistol, ammo, upgrade, and the entire rocket chain are listed in common.mapresources with the Immer bit. The pistol is thus broken from game start on every map equally.

Cause 2 (low confidence): The standard weapon class only fires hitscan. This contradicts the retail throwing hatchet: weapon/player/sp/hatchet inherits like handgun_nomesh from base_nomesh, has no inventoryClass, and fires via ammo.projectileDecl = projectile/player/axe_throw an Entity projectile. This is documented by the data, not by own runtime measurement. Only a test with the fixed Cause 1 rules it out.

Cause 3 (only important for the fix): Upgrades swap the projectile. With silencer or elephant rounds, the weapon fires pistol_silenced, pistol_elephant, or pistol_elephantrounds_silenced. Under Cause 1, these are also broken because they inherit from the pistol. A fix via ammo must bend them.

Excluded (measured)

  • Other copies and priority: All 30 .resources scanned (base, patch_1..4 including chunkbase, all chunks, dlc_0/1/2/3/5, also our chunk_22), every type, name containing player/pistol or handgun. projectile/player/pistol and ammo/player/sp/handgun exist exactly once each (gameresources), weapons/handgun/magazine_size twice (gameresources, patch_3). All copies are unpacked and carry the mod text. There is no compiled or binary second form.
  • data_hash +0x50: Exactly the 4 modified entries have an outdated hash (all remaining hits match). This is checked only via resourceStorage_checkDataCheckSum (live 0), and a failure would be a FatalError. The ammo change with an equally outdated hash came through.
  • Rocket chain not preloaded: kampf_pistol_rocket, projectile_ent/kampf_grenade_rocket, damage/player/kampf_grenade, and fx weapons/kampf/rocket are listed with ALWAYS in common and the c2v1 manifest.
  • Console command for inspection: listDecls, printDecl, and reloadDecls are registered but not among the 34 exposed commands (cmdtable.md). The console cannot display the Decl. The error path also does not write a warning, so dumpWarnings does not help.

Fix for Cause 1

Recommended (fits everywhere in place, no rebuild needed): Swap projectile via reference instead of via inherit. References may point forward: Retail has 3 such cases (upgrade valueDecl → later listed weapon, e.g., battlerifle/marksman), and they work. This is exactly the throwing hatchet pattern.

Asset Change new / space
projectile:projectile/player/pistol Remove entry from mod.json (back to Retail) –
ammo:ammo/player/sp/handgun edit.projectileDecl = projectile/player/kampf_pistol_rocket (+ clipSize 30) 482 / 512 B
upgrade:weapons/handgun/silencer edit.modifiersData.item[0].data.valueDecl = Rocket 635 / 640 B
upgrade:weapons/handgun/elephantrounds as file: patch_2-Retail text with valueDecl = Rocket (a set would take the DMT_UPGRADE_ANIM from the patch_2 copy) 701 / 704 B in both copies
upgrade:weapons/handgun/elephantrounds_silencer edit.modifiersData.item[0].data.valueDecl = Rocket 483 / 1216 B

Cost: The pistol adopts the thrower's firing sound, direct hit damage, and recoil. The silencer no longer makes it silent. This works on all maps.

Alternative (retains pistol sound and damage): inherit remains projectile/player/default, and the rocket fields are placed directly into the pistol's edit block (hitscan = false, notHitscanInfo{…}, splashDamageDecl, fxDecl, …). The silencer and elephant variants then inherit these. This totals 534 B (only what is necessary) or 786 B (everything) versus 448 B of space.

This means building new gameresources and a 702 MB backup on D:.

Not recommended: reordering the lines in common.mapresources (5 copies, global preload list, no asset type in the loader).

Inspector gap: tools/verify_weapon_mod.py checks if the parent is preloaded, but not if it comes before the child. This specific rule (parent line < child line in the highest common copy) would have reported this error as the sole violation under 299.

Test in-game (after fix)

  1. Restart the loader, apply the explosive pistol (new version), and start the game.
  2. Shoot at a wall with the pistol from more than 10 m distance. The rocket bounces off at approximately 5 m and ignites only after 3 s.

If it explodes, causes 1 and 2 are resolved: identical rocket Decl, but without inherit. If it does not explode, it is due to the weapon class (cause 2).

Without a new version, immediately in the current state: Does the pistol have almost no recoil or no tracer? Then it is also missing its parent default. This fits cause 1, but is only a soft indicator.

Re-verify

python tools/tnc_runtime_code.py --rva 0xa55000 --function   # inherit-parser, 0xA55342 / 0xA55416
python tools/tnc_runtime_code.py --rva 0xab8f80 --function   # 0xAB9224: reset + nested -> not loaded
python tools/tnc_runtime_code.py --rva 0xac1730 --function   # Phase 1 0xAC2FE0, Phase 2 0xAC3290

Order: parse_mrl (tools/probe_mapmanifest.py) on patch_4:generated/buildgame/common.mapresources, then for each Decl with inherit, compare the line number of the parent with its own.

Clone this wiki locally