Wolfenstein Modloader Buy me a coffeeKaffee spendieren

Wiki / Latest research roundsNeueste Forschungsrunden

Player Systems: Movement, Perks, Contraptions, Dual Wield, Weapon Upgrade Kits

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

In plain wordsIn einfachen Worten

This report studies the player systems of Wolfenstein II: The New Colossus (TNC); The New Order (TNO) is only cited from earlier file studies. Basic movement (run, sprint, jump, crouch height) is almost entirely hidden settings, 88 of them starting with pm_, so a movement mod needs the proven resourceExec route; perks, health caps, contraptions and weapon upgrades are text definitions the loader can edit. Perks switch on by themselves when a tracked count such as headshot kills passes a threshold; there are no skill points. The stock developer loadout already hands out all three contraptions (Battle Walker, Ram Shackles, Constrictor Harness) at once. Everything here is static, read from game files and code; only the reused magazine-size edit (pistol 10 to 30) and the resourceExec route were confirmed in game earlier, and none of the three new mods has been run.

  • 18 perk chains of 5 levels each switch on by themselves once a tracked count passes a threshold (e.g. 25 headshot kills); no skill points are spent.
  • Correction of pass 1: headshotter, triggerhappy, secretagent and heavymetal boost all their listed weapons (6, 5, 2 and 4), so no perk data is orphaned.
  • Movement is 88 pm_ cvars, nearly all hidden, so run, sprint and jump values need the resourceExec route proven in game; the exact values in the mod are untested.
  • The stock developer loadout lists all three contraptions together; whether a built-in limit (MAX_CONTRAPTIONS) then cuts this down is still open.
  • The magazine-size edit is confirmed in game (pistol 10 to 30); extending it to other guns, e.g. SMG 45 to 90, and the three mod designs are not tested in game.

Dieser Bericht untersucht die Spielersysteme von Wolfenstein II: The New Colossus (TNC); The New Order (TNO) wird nur aus früheren Dateistudien zitiert. Die Grundbewegung (Laufen, Sprinten, Springen, Duckhöhe) besteht fast nur aus versteckten Einstellungen, 88 davon beginnen mit pm_, eine Bewegungs-Mod braucht daher den bewährten resourceExec-Weg; Perks, Lebens-Obergrenzen, Kampfanzüge und Waffen-Upgrades sind dagegen Text-Definitionen, die der Loader ändern kann. Perks schalten sich von selbst frei, wenn ein gezählter Wert wie Kopfschuss-Kills eine Schwelle überschreitet; Skillpunkte gibt es nicht. Die mitgelieferte Entwickler-Ausrüstung verteilt bereits alle drei Kampfanzüge (Battle Walker, Ram Shackles, Constrictor Harness) auf einmal. Alles hier ist statisch, aus Spieldateien und Code gelesen; nur die wiederverwendete Magazingrößen-Änderung (Pistole 10 auf 30) und der resourceExec-Weg wurden früher im Spiel bestätigt, keine der drei neuen Mods wurde ausprobiert.

  • 18 Perk-Ketten mit je 5 Stufen schalten sich von selbst frei, sobald ein gezählter Wert eine Schwelle überschreitet (z. B. 25 Kopfschuss-Kills); Skillpunkte werden nicht ausgegeben.
  • Korrektur von Durchgang 1: headshotter, triggerhappy, secretagent und heavymetal verstärken alle ihre aufgeführten Waffen (6, 5, 2 und 4), es gibt also keine verwaisten Perk-Daten.
  • Bewegung besteht aus 88 pm_-Cvars, fast alle versteckt; Lauf-, Sprint- und Sprungwerte gehen daher nur über den im Spiel bewiesenen resourceExec-Weg, die konkreten Werte der Mod sind ungetestet.
  • Die mitgelieferte Entwickler-Ausrüstung enthält alle drei Kampfanzüge zusammen; ob eine eingebaute Grenze (MAX_CONTRAPTIONS) das dann beschneidet, ist noch offen.
  • Die Magazingrößen-Änderung ist im Spiel bestätigt (Pistole 10 auf 30); die Übertragung auf andere Waffen, etwa Maschinenpistole 45 auf 90, und die drei Mod-Entwürfe sind im Spiel nicht getestet.

Technical detailsTechnische Details

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

Round-6 research pass on TNC's player systems — movement, health/armor/regeneration, perks, the three contraptions, dual wielding and weapon upgrade kits — with TNO covered from the static corpus only. Movement is pure cvar state (pm_*, almost all non-exposed), while progression is decl-driven: stat-gated, auto-activating perk chains, upgrade decls that carry the actual modifier values, and Battle Walker, Ram Shackles and Constrictor Harness as ability items given to the player. Three ready-to-build decl mods are included. Static analysis only; none of the mods has been run in game.

Track playersys, round 6, 2026-10-01. Scope: mainly TNC (<Steam library>\steamapps\common\Wolfenstein.II.The.New.Colossus, common mount = gameresources.resources + highest patch chain patch_1..4, same precedence re_probes/newweapon/common_mount.py already established), TNO covered in §8 from the static corpus only — no runtime image exists for TNO (WolfNewOrder_x64.exe's .text is still SteamStub-encrypted, per the task brief and docs/ghidra.md), so every TNO claim here is a citation of docs/moddingflaeche-tno.md's own static reflection-table proof, not a re-derivation, and stays INFERRED/untested exactly as that doc already marks it. Read-only against both game installs and every project file outside this track's own re_probes/playersys/ and this report. The game was not launched, no debugger attached.

New code, all under re_probes/playersys/: scan_corpus.py (corpus census of perks/upgrade/abilityInfo/abilityInventoryItem/inventoryItem/ propItem/unlockable + entityDef:player, common-mount precedence, first-archive-wins) → out/*_all.txt (full decl text dumps, one per type), out/field_survey.json (per-type edit.* leaf-path union with counts); build_tables.py (new this pass, reads out/{perks,upgrade, abilityInfo}_all.txt back in, re-runnable, --self-check regression-tests the exact bug pass 1 had) → out/perk_table.txt (all 18 perk chains × 5 levels resolved to their modifier type/value progression, every weapon variant per chain, not just item[0] as pass 1's undocumented ad-hoc version did) and out/abilityInfo_compact.txt. Builds on, does not redo: re_probes/agent_reports/r3_newweapon.md (weapon decl anatomy, dualwieldable/maxCount, the three give-routes, the DMT_* enum glimpse), r4_pickups.md (devInvLoadout mechanism, the proven "redirect a map's devmapInvLoadout" recipe), r4_aidecl.md (the ClassName::ENUM_VALUE reflection-string cross-check method this report reuses for idUpgradeMod_*), docs/moddingflaeche-tnc.md §3.4/§3.7 (first naming of perks/upgrade/propItem/abilityInfo), memory tnc-decl-edits.md (the proven ammo:*.edit.clipSize lever), tnc-console-unlock.md (the proven resourceExec <cfile> -s route for non-exposed cvars/commands), re_probes/cvartable/cvars_tnc_v2.json (3256 cvars from the static idCVar-registrar list, itself already runtime-cross-checked against every constructor call site — I did not re-derive that, I only filtered it).

python re_probes/playersys/scan_corpus.py
python re_probes/playersys/build_tables.py   # new this pass

Pass 1 errors (fixed in this pass)

The verifier REFUTED one claim and listed five missed items. Status:

  1. REFUTED, now fixed — pass 1's headline "genuinely new finding" and §5.4/§5's "handgun only (N more orphaned)" cells for headshotter, triggerhappy, secretagent were wrong. Direct reads of out/perks_all.txt show headshotter_N.edit.upgrades.num=6, triggerhappy_N.num=5, secretagent_N.num=2, every item[] a live, retail-wired upgrade decl — none orphaned. Root cause: the ad-hoc script behind pass 1's out/perk_table.txt read only item[0]. Fixed with a new saved, re-runnable re_probes/playersys/build_tables.py (reads the full item[] array; --self-check asserts the 6/5/2 counts so this can't silently regress) and a regenerated out/perk_table.txt. heavymetal (num=4) had the same single-item blind spot — pass 1 didn't call it orphaned (it wasn't), but its table row also only showed one of four weapons; corrected too. See §5/§5.4 below.
  2. Fixed — pm_* cvar count corrected from "~60"/"60+" to the exact 88 (re_probes/cvartable/cvars_tnc_v2.json, direct count); extras named: pm_wheelchairfriction, pm_wheelchairheight, pm_wheelChairStrafeSpeedRatio. See §2.
  3. Fixed — named the concrete lean-adjacent cvar, player_analyzeEnvFreeLeanOnly (bool, default 0, exposed=false, "disables sliding along walls while not being able to lean"). Does not overturn "no numeric lean-distance cvar exists" — it's the nearest real lever, not a distance. See §2.
  4. Followed up — requiredStatForActivation.regions and the UI/display fields (criteria/reward/regionUnlockName/rewardScales/ presentationType/descriptionFlags) analyzed in new §5.5. Mostly the same INFERRED tier as the rest of requiredStatForActivation; one ambiguous lead (a bare "regions" string does carry a resolved reference, unlike every qualified requiredStatForActivation.* string — flagged, not promoted to PROVEN, see §5.5) and one new confirmation (presentationType_t/perkDescriptionFlags_t exist as real mangled RTTI enum-type names).
  5. Followed up — whether the (now-corrected) per-weapon perk-upgrades actually register per-weapon at runtime: no stronger proof than before exists in this snapshot, but a new, more specific piece of circumstantial evidence was found — a compiled debug format string "Upgrade %s Applyto %s\n" naming exactly the decl's own applyTo field semantics. See §5.4.
  6. Not re-attempted — a save-file probe of the real weapon-upgrade-kit purchase flag; still out of this track's scope (different territory, re_probes/savegame).

New, not in pass 1 or the verifier's list: found while fixing the item[] bug — player/perk/inconspicuous_3/4/5 declare modifiersData.num=3 but only populate item[0] (checked all 195 upgrade decls' three modifier arrays for the same num>populated-items gap: these 3 are the only true case corpus-wide). See §5.4.

Answer in one paragraph

TNC's player tuning splits cleanly into two layers that this track had to tell apart: pure engine state in cvars (all movement — pm_*, 60+ cvars for speed/accel/height/friction per stance, none of them decls, almost all exposed=False so only reachable via the already-proven resourceExec <cfile> -s route, never via +args) and decl-driven player progression (perks, contraptions, weapon upgrade kits, health/armor caps — all plain text, all editable with the loader's proven set/file patch mechanism). The progression layer resolves to 18 stat-gated perk chains (perks/player/sp/<name>_base + _1.._5, 111 decls total with 3 tutorial perks), each chain auto-activating (awardCondition=UC_AUTOMATIC, no manual skill-point spend) once a tracked stat crosses a threshold (requiredStatForActivation.statName/statIncrement, e.g. STAT_KILLS_HEADSHOT +25 for headshotter), and each level pointing at one upgrade:player/perk/<name>_N decl whose modifiersWeapon/modifiersAmmo/ modifiersData list carries the actual number — three real, reflection-visible C++ classes (idUpgradeMod_Weapon/idUpgradeMod_Ammo/ idUpgradeMod_Data, confirmed as qualified Class::ENUM strings in the runtime image) each owning one WMT_*/AMT_*/DMT_* enum. The three "contraptions" the task named are real, corpus-confirmed content (abilityInfo:battlewalker = Battle Walker, abilityInfo:ramshackles = Ram Shackles, abilityInfo:constrictorharness = Constrictor Harness — German Rammbock/Kobra-named internally), each with 3 subAbility0/1/2 + 1 sidequestAbility, granted by giving the player one abilityInventoryItem:abilities/{wanderer,rammbock,kobra} item; a compiled engine constant MAX_CONTRAPTIONS exists (name only, value unread, no runtime instance to query) hinting at a simultaneous-equip cap the game UI enforces, but the stock devinvloadout/code_referenced/default_sp_devloadout decl already lists all three ability items in its startingInventory simultaneously (item[10..12]) — meaning the exact redirect r4_pickups.md already proved for "every weapon" (point a map's world.edit.devmapInvLoadout at this decl) hands the player all three contraptions at once for free, as a bonus this track adds to that finding. Dual wield (r3_newweapon.md's decl-only dualwieldable=true + maxCount=2 finding) gets a fresh runtime cross-check here: the engine carries dedicated RTTI classes idDualwieldAnimAliasVariations/ idAnimSysNode_PlayDualwieldAnim/idAnimSysNodeData_PlayDualwieldAnim (mangled .?AV...@@ names, genuine referenced functions, not just decl text) and a tracked stat STAT_KILLS_DW feeding the triggerhappy perk chain — dual wield is real, wired, deep engine support, not a decl-only illusion. Weapon upgrade kits are a separate system from perks: each weapon decl carries edit.upgrades.item[0..N] naming 2-4 upgrade:weapons/<gun>/<mod> decls (e.g. handgun → magazine_size/silencer/elephantrounds/elephantrounds_silencer), bought with inventoryItem:unlocks/upgradekit currency (a stackable, non-droppable pickup, maxCount=100) at an in-game workbench — the purchase-gating flag itself was not found in static data (likely save-state, out of decl reach), so the safe, already-proven lever is not the upgrade-activation flag but the exact same edit.clipSize field on the base ammo:* decl that tnc-decl-edits.md already confirmed arrives in game (handgun 10→30, proven; this track extends the same lever to smg/battlerifle/shotgun/lkw/dkw, see §6 table). Correction from pass 1 (see "Pass 1 errors" above): three weapon-perk chains that look single-weapon by name (headshotter, triggerhappy, secretagent) are not single-weapon and nothing in this corpus is orphaned — headshotter_N.edit.upgrades already lists all 6 weapon variants (handgun/smg/shotgun/battlerifle/kampfpistol/laserkraftwerk), triggerhappy_N all 5, secretagent_N both of its 2; heavymetal_N was already wired to 4 weapons too. Every one of these perk levels buffs every listed weapon simultaneously, out of the box, in retail — pass 1's "orphaned content, wire it back in" mod idea (§5.4) does not apply; §5.4 is rewritten below to show the real per-weapon value tables instead.

1. Method and corpus

re_probes/playersys/scan_corpus.py mounts gameresources.resources + patch_4..1 (common-mount precedence, first archive wins — identical methodology to re_probes/newweapon/common_mount.py) and collects every entry of 7 decl types plus entityDef:player:

perks: 111 decls           abilityInventoryItem: 24 decls
upgrade: 195 decls         inventoryItem: 10 decls
abilityInfo: 36 decls      propItem: 52 decls
                           unlockable: 84 decls
entityDef:player bytes: 13165

All 508 decls parsed with wolfsdk/decl.py's Decl() with zero DeclError (consistent with docs/moddingflaeche-tnc.md's corpus-wide "44 140/44 374 parse clean" headline number — these 7 types are fully inside that safe set). edit.* leaf-path unions per type are in out/field_survey.json.

2. Movement: cvars, not decls — pm_* and friends

Everything that moves the player's capsule is a cvar, not a decl field. Filtered from re_probes/cvartable/cvars_tnc_v2.json (3256 cvars from the static idCVar-registrar list at RVA 0x30B58F8, itself already cross-checked against every constructor call site — that grounding predates this track, I only filtered the table). Exact count: 88 pm_* cvars (pass 1 said "~60"/"60+", undercounted — corrected this pass; unmentioned extras beyond the table below: pm_wheelchairfriction, pm_wheelchairheight, pm_wheelChairStrafeSpeedRatio):

Family cvar default meaning
speed pm_walkspeed 2.8 sneak/walk
pm_runspeed 5.2 normal run
pm_sprintspeed 9.0 sprint
pm_sprintspeedUnarmed 6.5 sprint, no weapon out
pm_backSpeedRatio 0.75 backward-move ratio
pm_strafeSpeedRatio 0.85 strafe ratio
pm_crouchbasespeed 1.75 crouched move
pm_pronesprintspeed 2.2 prone sprint
pm_wandererspeed / pm_wanderersprintspeed 3.6 / 4.5 Battle Walker mech-legs mode
pm_wheelChairSpeed / …printspeed 1.5 / 2.3 forced wheelchair sequence only
jump pm_jumpheight 1.15 normal jump apex
pm_jumpHeightPanzerRide 1.15 while riding a Panzerhund (scripted)
pm_jumpheightWheelChair 0.15 wheelchair sequence
pm_groundpoundvelocity -2.0 Battle Walker ground-pound impact speed
crouch pm_crouchheight/pm_crouchviewheight 0.89 / 0.80 capsule/eye height crouched
pm_normalheight/pm_normalViewHeight 1.79 / 1.54 standing
pm_proneheight 0.29 prone
sprint-slide pm_sprintSlideLowerSpeed 4.0 slide stop speed
pm_sprintslidefriction 1.5 slide friction
Ram Shackles pm_rammbockactivationtime 200 ms sprint-hold before tackle arms
pm_rammbockDotChecker -0.69 facing-angle gate for a valid tackle hit
mantle/traversal dp_jumpTraversalMinApexHeight 32.0 min apex for an auto-traversal jump
pm_stepsize 0.32 auto step-up height (no separate "mantle" cvar found)
lean (none numeric) — player_analyzeEnv* runs lean through the cover/environment analyzer, not a pm_lean* amount — no dedicated lean-distance cvar exists (still true this pass); nearest real, citable lever: player_analyzeEnvFreeLeanOnly (bool, default 0, exposed=false, "disables sliding along walls while not being able to lean") — a behavior toggle, not a distance
regen/health g_playerHealthRegenDelay 3 s wait after damage before regen starts
g_playerHealthRegenSpeed 5.75 hp/s
g_playerShieldRegenDelay/Interval/Rate 5 / 0.5 / 0 armor-shield regen (rate is 0 by default — off)
difficulty g_playerHealthAndArmorScale_DIFFICULTY_* (7) 1.0 each per-tier player HP/armor multiplier
g_playerIncomingDamageScale_DIFFICULTY_* (7) 1.0 each per-tier incoming-damage multiplier

All of these are exposed=False except pm_togglesprint, pm_enableQuickTurn (both PC-option cvars, genuinely console-settable). Every other one needs the proven resourceExec <loader-replaced cfile> -s route (tnc-console-unlock.md) to set at all — +pm_sprintspeed 14 as a launch arg is rejected exactly like the already-tested +pm_autoSprint was. This is the key finding for a "Movement+" mod: it cannot be a pure decl mod. §7.1 gives both a decl-only partial (crouch/stealth speed, regen, health/armor caps — genuinely decl-driven, see §3) and the cvar-cfile supplement for raw run/sprint/jump numbers.

3. Health, armor, regen — decl-driven, proven mechanism

entityDef:player.edit.actorEditable.healthOverrides (num=2) names two healthComponent decls applied at spawn:

item[0] = "player/health_force_100"   -- healthComponent, edit.max=100 edit.cur=100
item[1] = "player/armor_max_100"      -- healthComponent, edit.max=100 edit.component="COMPONENT_ARMOR"

A second, unused-by-default pair already ships in retail: player/health_force_50 (max/cur 50, a "half-health" variant, likely a specific chapter/hardcore-mode override) and player/armor_max_200 (max=200, component="COMPONENT_ARMOR") — the latter is the exact decl the abilityInfo:armorboost ("Repairman"/"Armor Boost" sidequest ability, AT_ARMORBOOST) raises the cap to once unlocked; its abilityInventoryItem:abilities/armorovercharge carries no explicit numeric field itself (just abilityInfo="armorboost"), so the 100→200 swap is almost certainly C++-side logic switching which healthComponent applies once that ability is owned, not a second decl path — INFERRED, not static-provable without the ability actually active in a save. entityDamage:player/testplayerdamage is the player's single damage-group decl (one group "body", one scalar "base" — much flatter than AI's per-bodypart aiHealth structure from r4_aidecl.md; player damage scaling lives in the separate playerdamage/* difficulty-table decls playerEditable.{easy,normal,hard,...}Table already documented by that report's sibling work, not re-measured here).

Regen/decay speed and crouch/stealth-move speed ARE decl fields, via the perk-upgrade chain (§5): DMT_HEALTH_REGEN_SPEED (frontalassault), DMT_HEALTH_DECAY_SPEED (hardysoldier — lower is better, it is the delay before regen kicks in, not a regen rate), DMT_CROUCH_SPEED (assassin), DMT_KOBRA_MOVE_SPEED (kobra/Constrictor Harness prone-crawl speed).

Runtime cross-check: healthOverrides is the one field name in this whole track whose compiled string carries an actual reference (0x7ff62d19ad8b, not inside any disassembled function body — a .rdata reflection-table pointer, same pattern r4_aidecl.md established for aiEditable/perception: "exists as a compiled string with exactly one reflection-table pointer reference, proving the engine's decl-binding schema knows the field"). requiredStatForActivation/autoActivate/ lockedFromStart exist as compiled strings too but with no resolved reference in this snapshot (the x86 Constant Reference Analyzer does not always resolve indirect reflection-table loads — same caveat r4_aidecl.md flagged); their presence as exact-match compiled strings is still evidence, just weaker than a resolved pointer — marked INFERRED below, not PROVEN.

4. Dual wielding — decl mechanism confirmed, now with runtime backing

r3_newweapon.md §1.9 already established the decl mechanism exactly: dualwieldable=true + maxCount=2 on the same weapon decl, no second decl, true for every actually-equippable dual-wieldable weapon (handgun/smg/shotgun/battlerifle variants/kampfpistol). This track adds the runtime side, via python tools/decomp.py --strings:

"idPlayer::NODEINDEX_BODYREACTION_NONE_DUALWIELD"
"idPlayer::NODEINDEX_BODYREACTION_NONE_WANDERER_DUALWIELD"   <- dual wield + Battle Walker stack
"idPlayer::NODEINDEX_BODYREACTION_CAMERASHAKE_DUALWIELD"
".?AVidDualwieldAnimAliasVariations@@"                       <- real RTTI class, mangled name
".?AVidAnimSysNode_PlayDualwieldAnim@@"                       <- real RTTI class
".?AVidAnimSysNodeData_PlayDualwieldAnim@@"                   <- real RTTI class
"idAnimSysNode_PlayDualwieldAnim::Init - Alias: '%s' not found in node: '%s' for animSys '%s'"
    <- FUN_7ff62cfd8130   (a real error-formatting call site)
"aiEditable.actionSettings.combat.canDualWield"               <- AI can be flagged dual-wield too
"actorConstants.canDualWield"

Three genuine, mangled-name RTTI classes exist purely to drive dual-wield animation blending, plus a confirmed node-index enum for camera-shake and Battle-Walker-combined body reactions — this is deep, dedicated engine support, not a thin decl flag the engine barely reads. The tracked stat STAT_KILLS_DW (feeding the triggerhappy perk, see §5) further confirms dual-wield kills are counted server-side, not just animated.

5. Perks — 18 stat-gated chains, auto-activating, no skill points

entityDef:player.edit.perkCollection lists exactly 21 chains (18 real + 3 tutorial), each {perks.item[0..4] = "perk/player/sp/<name>_1..5"}. Every chain: a _base decl (display/icon strings + requiredStatForActivation) plus five level decls, each just {inherit=_base; edit.upgrades.item[0] = "player/perk/<name>_N"}. awardCondition="UC_AUTOMATIC" on all 158 measured upgrade decls that have it, and reUsable=true — there is no skill-point currency, no manual spend: a perk auto-activates the instant its tracked stat crosses the threshold. Full resolved table (re_probes/playersys/out/perk_table.txt, stat → increment → modifier type → level 1→5 values):

chain stat (increment) type applies to 1 → 5
assassin STAT_KILLS_STEALTH_TAKEDOWN (10) DMT_CROUCH_SPEED player 1.90 → 2.85
axeman STAT_KILLS_THROWN_AXE (10) AMT_AMMO_MAX_COUNT hatchet 4 → 8
electromaniac STAT_KILLS_STUNNED (2) DMT_ELECTRIC_DAMAGE_SCALER player 0.95 → 0.75
frontalassault STAT_KILLS_TAKEDOWN (10) DMT_HEALTH_REGEN_SPEED player 2.1 → 2.5
grenadier STAT_KILLS_GRENADE (25) AMT_AMMO_MAX_COUNT grenade_frag 4 → 8
hardened STAT_KILLS_ENVIRONMENT (10) DMT_EXPLOSION_DAMAGE_SCALER player 0.95 → 0.75
hardysoldier STAT_KILLS_WHILE_HEALTH_OVERCHARGE (20) DMT_HEALTH_DECAY_SPEED player 1.8 → 1.0
headshotter STAT_KILLS_HEADSHOT (25) WMT_WEAPON_DAMAGE_IRON_SIGHT_DAMAGE_SCALE all 6 (handgun/smg/battlerifle/shotgun/kampfpistol/lkw, §5.4) 1.05 → 1.25 (same curve, all 6)
heavymetal STAT_KILLS_HEAVY_WEAPON (20) AMT_AMMO_MAX_COUNT all 4 (lasergewehr/hammergewehr/dieselgewehr/ubergewehr, §5.4) varies per weapon, §5.4
inconspicuous STAT_COLLECT_ENIGMA_CODE (10) DMT_COMMANDER_ALARM_TIME player 0.2 → 1.0
kobra (Constrictor) STAT_KILLS_CONSTRICTOR_TAKEDOWN (1) DMT_KOBRA_MOVE_SPEED player 1.42 → 1.88
pyromaniac STAT_KILLS_FIRE (10) DMT_FIRE_DAMAGE_SCALER player 0.95 → 0.75
rammbock (Ram Shackles) STAT_KILLS_RAM (10) DMT_ARMOR_LOOT_SCALER player 1.1 → 1.5
refused STAT_KILLS_ENEMY_GRENADE (2) DMT_GRENADE_EXTRA_DETONATION_DELAY player 0.5 → 2.0
saboteur STAT_ROBOT_TAKEDOWN_OR_TAMPER (2) DMT_TAMPER_AMMO_VALUE player 1 → 5
secretagent STAT_KILLS_COMMANDER_NA (5) WMT_WEAPON_DAMAGE_SUPPRESSED both 2 (handgun/smg, §5.4) 1.1 → 1.5 (same curve, both)
triggerhappy STAT_KILLS_DW (50) AMT_AMMO_MAX_COUNT all 5 (handgun/smg/battlerifle/shotgun/kampfpistol, §5.4) varies per weapon, §5.4
wanderer (Battle Walker) STAT_KILLS_BATTLE_WALKER (20) DMT_AMMO_LOOT_SCALER player 1.1 → 1.5

opType is MOD_OPERATOR_SET on every measured modifier (no additive mode seen in this corpus). heavymetal/headshotter/secretagent/ triggerhappy each carry multiple per-weapon upgrade decls (heavymetal_{dieselgewehr,hammergewehr,lasergewehr,ubergewehr} etc.) and the perk's upgrades array references all of them, applying the modifier to every listed weapon at once (corrected from pass 1, which misread only item[0] and wrongly called the rest orphaned — see "Pass 1 errors" above and §5.4's full per-weapon table).

5.1 Runtime cross-check: idUpgradeMod_*

python tools/decomp.py --strings "DMT_ARMOR_LOOT_SCALER|DMT_KOBRA_MOVE_SPEED|DMT_HEALTH_REGEN_SPEED"
  "idUpgradeMod_Data::DMT_KOBRA_MOVE_SPEED"
  "idUpgradeMod_Data::DMT_HEALTH_REGEN_SPEED"
  "idUpgradeMod_Data::DMT_ARMOR_LOOT_SCALER"

python tools/decomp.py --strings "WMT_WEAPON_DAMAGE_IRON_SIGHT_DAMAGE_SCALE|AMT_AMMO_MAX_COUNT|MOD_OPERATOR_SET"
  "idUpgradeMod_Weapon::WMT_WEAPON_DAMAGE_IRON_SIGHT_DAMAGE_SCALE"
  "idUpgradeMod_Ammo::AMT_AMMO_MAX_COUNT"

Every DMT_*/WMT_*/AMT_* tested resolves to a qualified idUpgradeMod_{Data,Weapon,Ammo}::ENUM_VALUE string — three real, distinct C++ reflection classes, one per modifier list on the upgrade decl, matching the decl structure (modifiersData/modifiersWeapon/ modifiersAmmo) field for field. No resolved code xref in this snapshot (reflection-table load, same caveat as §3) — PROVEN that the type exists and is named exactly as the decl text names it; INFERRED (not re-derived here) that a given numeric value is actually read every frame, consistent with how r4_aidecl.md treated the same class of evidence for AI fields.

5.2 Auto-activation: PROVEN string, no resolved xref

requiredStatForActivation, autoActivate, lockedFromStart all exist as exact compiled strings (§3) but with no resolved .rdata xref in this snapshot. INFERRED, not PROVEN, that the engine evaluates them exactly as named — strong (exact string match, matches the decl schema field for field, same class of evidence r4_aidecl.md accepted for weaker cases) but short of the stronger "resolved pointer" bar this report applies to healthOverrides.

5.3 lockedFromStart — only the 3 contraption perk bases

edit.lockedFromStart appears on exactly 3 of 111 perks: kobra_base, rammbock_base, wanderer_base — the three contraption perk chains, consistent with them being gated behind actually owning that contraption (a story choice), unlike the other 15 chains which are available from the start and simply accumulate their stat during normal play.

5.4 Corrected from pass 1: the 4 multi-weapon chains, nothing orphaned

Pass 1 claimed headshotter_N.edit.upgrades had num=1 and 25+20+5=50 per-weapon decls were "orphaned". Wrong — direct reads of out/perks_all.txt show num=6/4/2/5 for these four chains and every item[] resolves to a real, well-formed upgrade decl (verifier-confirmed for 3 of the 4; heavymetal's num=4 checked the same way this pass). Regenerated with re_probes/playersys/build_tables.py (full item[] walk, fixes pass 1's item[0]-only bug), full per-weapon tables:

headshotter (STAT_KILLS_HEADSHOT, incr 25) — all 6, same curve:
  handgun/smg/battlerifle/shotgun/kampfpistol/lkw
  WMT_WEAPON_DAMAGE_IRON_SIGHT_DAMAGE_SCALE: 1.05→1.1→1.15→1.2→1.25

secretagent (STAT_KILLS_COMMANDER_NA, incr 5) — both 2, same curve:
  handgun/smg
  WMT_WEAPON_DAMAGE_SUPPRESSED: 1.1→1.2→1.3→1.4→1.5

triggerhappy (STAT_KILLS_DW, incr 50) — all 5, per-weapon curves:
  handgun       AMT_AMMO_MAX_COUNT: 90→100→110→120→130
  smg           AMT_AMMO_MAX_COUNT: 405→450→495→540→585
  battlerifle   AMT_AMMO_MAX_COUNT: 270→300→330→360→390
  shotgun       AMT_AMMO_MAX_COUNT: 180→200→220→240→260
  kampfpistol   AMT_AMMO_MAX_COUNT: 13→14→16→17→18

heavymetal (STAT_KILLS_HEAVY_WEAPON, incr 20) — all 4, per-weapon curves:
  lasergewehr   AMT_AMMO_MAX_COUNT: 240→280→320→360→400
  hammergewehr  AMT_AMMO_MAX_COUNT: 240→280→320→360→400
  dieselgewehr  AMT_AMMO_MAX_COUNT: 60→70→80→90→100
  ubergewehr    AMT_AMMO_MAX_COUNT: 12→14→16→18→20

So all four "look single-weapon by name" chains are, in retail, already multi-weapon perks: one stat threshold buffs every listed gun at once. No mod is needed to "wire the orphaned decls back in" (pass 1's §5.4 idea is withdrawn) — there is nothing orphaned to wire.

Did not change: whether the engine actually applies each per-weapon modifier at runtime is still not resolved to a pointer-level PROVEN (same caveat as §5.1/§5.2). One new, more specific piece of circumstantial evidence found this pass: python tools/decomp.py --strings applyTo turns up a compiled debug/log format string, "Upgrade %s Applyto %s\n" (0x7ff62eace9d8), whose text literally mirrors the decl's own edit.applyTo field ("Upgrade <name> Applyto <weapon>") — strong circumstantial evidence real code logs "which upgrade, applied to which weapon" at some point, even though (same Constant Reference Analyzer limitation as everywhere else in this report) the string itself has no resolved caller in this snapshot. Still INFERRED, not PROVEN, but a stronger lead than a bare field-name string alone.

New, unrelated-to-the-bug finding from the same fix: upgrade decls' modifiersWeapon/modifiersAmmo/modifiersData arrays declare a num and then enumerate item[0..num-1] — checked all 195 upgrade decls for cases where num exceeds the populated item[] count (the same shape of bug pass 1 had, now checked against the retail data itself, not just the parser): exactly 3 true cases, all in one chain — player/perk/inconspicuous_3/4/5 each declare modifiersData.num=3 but only populate item[0]; item[1]/item[2] are fully absent from the decl text (not just missing an optional sub-field, as 17 other num>1 decls in the corpus legitimately do by omitting only type on an otherwise-present item[], e.g. weapons/smg/silencer.item[0] has no type but does have data.valueDecl). Whether the engine reads out-of-bounds, zero-fills, or simply ignores item[1..2] for these three inconspicuous levels is unverified — flagged, §11.

5.5 The perk _base UI/display layer and requiredStatForActivation.regions

Missed by pass 1, present in its own field_survey.json: every _base decl (18 real chains + 3 tutorial) also carries a UI/display field set parallel to the gameplay fields already covered — criteria, reward, regionUnlockName (all #str_* localization keys), presentationType ("Percent" on every chain checked), descriptionFlags ("PKF_PLAYER_ABILITY" on every chain checked), and rewardScales (a num-sized float array, always 5 entries, values like [5,10,15,20,25] for headshotter, matching the 5 perk levels — read as per-level percentage labels for a progress bar, e.g. "25% to next headshot perk level", cosmetic, not re-derived as gameplay-affecting). Separately, requiredStatForActivation carries a regions sub-array (field_survey.json confirms it on 19 of 21 _base decls) that pass 1's report never named — example, headshotter_base: statIncrement=25 but regions={num=5; item=[1,5,10,15,20]}; other chains have num from 1 to 5 and values up to ~40, always starting at 1. These are not chapter/level indices (too many/too irregular for TNC's actual campaign structure) — most likely sub-thresholds for the UI's own progress ticker (intermediate counts worth a HUD/journal notification before the full statIncrement is reached), but this is a naming-based guess, not verified against any UI code path.

Runtime cross-check, python tools/decomp.py --strings <name>:

"requiredStatForActivation.regions"   -> no references found   (qualified path, same as
"requiredStatForActivation.statName"  -> no references found    every other qualified
"requiredStatForActivation.statValue" -> no references found    requiredStatForActivation.*
"requiredStatForActivation.statIncrement" -> no references found  string, consistent with §5.2)
"requiredStatForActivation.statValueStart" -> no references found  (decl field, never seen
                                                                     populated in this corpus)

"regions" (bare, no prefix)  -> ONE resolved reference: 0x7ff62d9e5a0c
    (not inside any disassembled function body, same ".rdata pointer, no
    FUN_ label" shape the report's healthOverrides finding already used)

"rewardScales"/"presentationType"/"descriptionFlags"/"regionUnlockName"/
"criteria"/"reward" (bare) -> no references found

"presentationType_t"  -> no references found, BUT
".?AW4presentationType_t@@"      -> real mangled RTTI enum-type name
"perkDescriptionFlags_t" -> no references found, BUT
".?AW4perkDescriptionFlags_t@@"  -> real mangled RTTI enum-type name

Two results worth separating: (a) presentationType_t and perkDescriptionFlags_t exist as genuine mangled RTTI enum types (.?AW4..@@), the same "named C++ reflection class" evidence tier §5.1 already uses for idUpgradeMod_* — this is real, typed engine support for the UI layer, not just raw decl text. (b) The bare "regions" string's single resolved reference looks, on its face, like the same proof tier that promoted healthOverrides to PROVEN in §3 — but it is not promoted here: "regions" is a generic word a reflection system could easily reuse for an unrelated field on a different decl type (string literals get interned/deduplicated by the compiler, so one address can back multiple, unrelated reflection-table entries), and the fully-qualified "requiredStatForActivation.regions" string — which would be unambiguous — itself has no reference. Recorded as an ambiguous lead, not a proof; a follow-up would need to find what structure actually points at 0x7ff62d9e5a0c (data, not a function, so decomp.py's function-level tools can't resolve it further) to disambiguate it from an unrelated system. Net effect: regions/rewardScales/presentationType/ descriptionFlags/criteria/reward/regionUnlockName all stay INFERRED, same bucket as requiredStatForActivation/autoActivate/ lockedFromStart in §5.2 — this pass adds detail, not a tier change.

6. Weapon upgrade kits — separate from perks, same upgrade decl type

Every weapon decl's own edit.upgrades.item[0..N] (2-4 entries) names upgrade:weapons/<gun>/<mod> decls, bought with inventoryItem:unlocks/upgradekit currency (maxCount=100, stackable, usable=false — pure currency, no direct-use item) at an in-game workbench. Example, handgun (weapon/player/sp/handgun_nomesh.edit.upgrades, num=4): magazine_size, silencer, elephantrounds, elephantrounds_silencer. Each upgrade decl carries startActivated=true and an upgradeNum ordinal (1/2/3) in every sample checked — this looks like "once purchased, active immediately, no extra toggle" rather than "active from a fresh save"; the actual purchase/owned-flag is almost certainly save-state, not found in any scanned decl — not claimed here. toggable=true appears only on elephantrounds (switchable ammo type once owned). weapons/handgun/silencer.edit.modifiersWeapon is a clean WMT_WEAPON_SUPPRESSED + two WMT_WEAPON_HEAT_* entries — confirms tnc_waffe_explosivpistole's own finding that the silencer's suppression is a decl-level weapon-state flag, not hardcoded.

Safe, proven lever (reuses tnc-decl-edits.md's exact field, not the unverified activation flag): ammo:*.edit.clipSize, before vs. each weapon's own magazine_size upgrade target:

weapon retail clipSize (current install) maxCount upgraded target (AMT_AMMO_CLIP_SIZE)
handgun 30 (already patched in this install, see note) 80 30
smg 45 360 90
battlerifle 30 240 60
shotgun 20 160 40
lkw 100 100 200
dkw 10 10 20

Note: the live <Steam library>\...\gameresources.resources already reads ammo/player/sp/handgun.edit.clipSize=30 — i.e. 10 (vanilla, per tnc-decl-edits.md's own before/after) → 30, exactly the already-proven tnc_waffe_explosivpistole mod's value, meaning that mod (or an equivalent hand-edit) is still applied to this read-only install from a prior session. Flagged, not touched (read-only rule) — the other 5 rows above are this install's genuine, un-modified retail baseline.

7. Contraptions — Battle Walker, Ram Shackles, Constrictor Harness

Confirmed by name search across all 7 scanned types (§1):

in-game name abilityType abilityInfo decl subAbility0/1/2 sidequestAbility
Battle Walker AT_BATTLEWALKER battlewalker legextension, quickdraw, adrenaline healthy
Ram Shackles AT_RAMSHACKLES ramshackles supertackle, steadyfeet, pitcher repairman
Constrictor Harness AT_CONSTRICTHARNESS constrictorharness slither, silentfeet, wallflower holdbreath

Each DLC protagonist gets a character-reskinned variant with swapped sub-abilities, confirmed in the same decl type: battlewalker_wilkins→AT_CHRONICLE_WILKINS (subs: healthy_wilkins, adrenaline_wilkins, quickdraw_wilkins — "The Amazing Deeds of Captain Wilkins"), silentfeet_shade→AT_CHRONICLE_SHADE (subs: terrorbilly_shade, adrenaline_shade, quickdraw_shade — "The Diaries of Agent Silent Death"), supertackle_joe→AT_CHRONICLE_JOE (subs: steadyfeet_joe, repairman_joe, pitcher_joe — "The Adventures of Gunslinger Joe"). watchtower (requirement="battlewalker") is a fourth, non-contraption ability chained off Battle Walker — not part of the three-choice itself.

Grant mechanism, confirmed end to end: propItem:ability/{kobra,rammbock,wanderer} (world pickup prop, each inventoryDecl pointing at the matching abilityInventoryItem) → abilityInventoryItem:abilities/{kobra,rammbock,wanderer} (inventoryClass="idAbilityInventoryItem", abilityInfo="<name>") → the abilityInfo decl's subAbility* list becomes available. Giving the player the abilityInventoryItem directly (devInvLoadout route, §7.2) skips the world prop entirely — same mechanism class r4_pickups.md already proved for weapons.

A compiled constant MAX_CONTRAPTIONS exists (python tools/decomp.py --strings MAX_CONTRAPTIONS, no resolved xref, string only) — INFERRED, not measured, that this caps simultaneous active contraptions at some number (presumably 2, matching the game's known "choose 2 of 3" late-game beat); it does not by itself prove owning all 3 ability items simultaneously is rejected, crashes, or silently caps — genuinely open, see §9/ingame_test.

7.1 All three are already co-present in retail data

devinvloadout/code_referenced/default_sp_devloadout (startingInventory.num=21) already lists, unconditionally:

item[10] = { item = "abilities/kobra"; }
item[11] = { item = "abilities/wanderer"; }
item[12] = { item = "abilities/rammbock"; }

— all three contraption ability items, together, in the stock decl the dev tools ship. This is the single strongest piece of evidence in this report that holding all three at once is not structurally rejected by the data layer (whether the engine enforces MAX_CONTRAPTIONS on top is still open, §9).

8. TNO — static corpus only, cited not re-derived

No runtime image exists for TNO; every item below is docs/moddingflaeche-tno.md's own already-published, already reflection-table-cross-checked finding (its own methodology note: field schemas read directly from static reflection tables at fixed file offsets, e.g. idDeclGameModeSP's schema at 0xce6127), marked there as "vermutlich/ungetestet" (game not launched) and kept that way here:

  • Health/armor start values: gamemodesp:gamemode/campaign (startingHealth=100, startingArmor=0, no gameModeFlags field present at all) vs. the four bonus modes, e.g. gamemode/999 (startingHealth=999, FLAG_AMMOUNLIMITED|FLAG_NOOVERCHARGE). Five-value flag enum confirmed complete from the binary's string pool: FLAG_NONE/AMMOUNLIMITED/HIDEHUD/ONELIFE/NOOVERCHARGE/ HEALTHARMORREMOVED. startingHealth=999 on campaign needs a file replace (field absent from that decl's own text, set cannot add it); the cvar route (g_infiniteAmmo) is independently confirmed blocked in retail (wolfsdk/cvars.py::RETAIL_BLOCKED), so the decl route is the only one.
  • Movement-adjacent fields live partly in the player decl itself, unlike TNC: playerHealth.components.components[1].cur, maxTimeUnderwater.value=50000, sprintAirReductionScale=1, suffocationDamage — all on the player entityDef, all unused by any shipped mod in this repo yet. A real engine/decl split difference from TNC worth noting: TNC moved almost all movement into cvars and kept only the ability-modifier layer (DMT_*) in decls; TNO still keeps a slice of movement tuning directly on the player decl.
  • playerUpgradeAmmo (12 decls, slots[N] = {type="AMMOTYPE_LIGHT"; amount=10;}) — TNO's equivalent of TNC's AMT_AMMO_MAX_COUNT perk levels, flat set edits.
  • weaponUpgrade/laserCutterUpgrade (17 decls) — TNO's equivalent of TNC's per-weapon upgrade:weapons/* kits; weaponToUpgrade can be re-pointed at a different weapon decl (a "mod transplant" exactly like TNC's valueDecl swap technique).
  • Dual wield: docs/modding.md already shows the lever, edit.isDualWieldable=true on any weapon:* decl (TNO's field name differs from TNC's dualwieldable, otherwise the same idea) — no per-weapon maxCount=2 requirement documented for TNO, unconfirmed whether TNO needs it too (not re-tested here, out of scope for this TNC-focused track).

9. PROVEN / INFERRED / FAILED

PROVEN (runtime-grounded, this track or an already-runtime-proven prior one):

  • ammo:*.edit.clipSize arrives in game (handgun 10→30, tnc-decl-edits.md, user-confirmed screenshot).
  • resourceExec <loader-replaced cfile> -s executes non-exposed commands/cvars (tnc-console-unlock.md) — the mechanism this report's cvar-based Movement+ layer depends on.
  • dualwieldable=true+maxCount=2 is the complete decl-level dual-wield switch, no second decl needed (r3_newweapon.md, corpus-measured, 16/16 equippable weapons consistent).
  • idUpgradeMod_{Data,Weapon,Ammo} are real, named C++ reflection classes matching the decl's modifiersData/modifiersWeapon/modifiersAmmo structure field-for-field (this track, --strings on 5 enum values from 3 different modifier families, 5/5 hit).
  • Dual wield has dedicated RTTI classes (idDualwieldAnimAliasVariations, idAnimSysNode(Data)_PlayDualwieldAnim) and a node-index enum including a Battle-Walker-combined state — genuine engine depth, not a decl-only illusion (this track).
  • healthOverrides has a resolved .rdata reference (this track) — the strongest cross-check tier this report reaches for a player-decl field.
  • All 18 perk chains, their stat/increment/modifier-type/level values are exactly as the corpus states them (pure static parse, no ambiguity — "proven" in the sense of "correctly read", not "confirmed to fire in game").
  • devinvloadout/code_referenced/default_sp_devloadout already contains all 3 contraption ability items simultaneously, in retail, unmodified.
  • This pass: headshotter/triggerhappy/secretagent/heavymetal each buff all of their listed weapons (6/5/2/4), not just one — direct corpus read, regression-checked by build_tables.py --self-check (corrects pass 1's refuted "orphaned decls" claim, §5.4).
  • This pass: exact pm_* cvar count is 88 (corrects pass 1's "~60").
  • This pass: presentationType_t/perkDescriptionFlags_t exist as real mangled RTTI enum-type names (.?AW4presentationType_t@@, .?AW4perkDescriptionFlags_t@@) — the perk UI/display layer has genuine typed engine reflection support, not just raw decl text (§5.5).

INFERRED (name/schema match or reflection string exists, but no resolved runtime pointer, or not tested in a live save):

  • requiredStatForActivation/autoActivate/lockedFromStart are read exactly as named (exact compiled strings, no resolved xref this run).
  • armor_max_200 is what AT_ARMORBOOST swaps in (no explicit numeric link found in any decl; inferred from naming + the ability's existence).
  • startActivated=true on upgrade-kit decls means "active once purchased, no extra toggle" rather than "active from a fresh save" — the actual purchase/owned flag was not located in any scanned decl (almost certainly save-state).
  • MAX_CONTRAPTIONS caps simultaneous active contraptions (name only, no value, no resolved xref) — does not by itself say what happens if a mod hands the player all 3 ability items at once (the in-game test below answers this for the maintainer).
  • All TNO findings (§8) — static-only by project-wide constraint, not a gap this track could close.
  • This pass: the per-weapon perk-upgrade modifiers (headshotter etc.) actually apply at runtime per weapon — no resolved pointer either way, but a compiled debug string "Upgrade %s Applyto %s\n" matching the applyTo decl field's own semantics is new circumstantial support (§5.4).
  • This pass: requiredStatForActivation.regions and the perk _base UI fields (rewardScales, criteria, reward, regionUnlockName) are read as named — same tier as requiredStatForActivation itself; a bare "regions" string's single resolved reference is flagged as an ambiguous lead, not promoted (string interning could mean it belongs to an unrelated reflection entry elsewhere in the engine), §5.5.

FAILED hypotheses, this track:

  • Expected requiredStatForActivation/autoActivate/lockedFromStart to resolve a .rdata xref the same way healthOverrides did (same search pattern, same snapshot) — they did not; the Constant Reference Analyzer simply doesn't resolve every reflection-table load, confirmed dead end, not pursued further (budget).
  • Expected FUN_7ff62c491bd0 (referenced by the string "PlayerDualWield") to be the actual dual-wield gameplay check — it decompiles to a 4-line stat/telemetry-label registration call (FUN_7ff62d40b8c0(…, s_PlayerDualWield…)), not the mechanic itself; the real mechanic is in the unlabeled idAnimSysNode_PlayDualwieldAnim-driven animation node graph, not pursued further into its vtable (budget, out of scope for a decl-focused track).
  • Looked for a numeric pm_lean* cvar (expected, given lean controls exist per the task's movement list) — none exists; lean is driven by the player_analyzeEnv* cover/peek system, not a free, tunable lean amount. Confirmed dead end (exhaustive pm_/player_ substring filter over all 3256 cvars), recorded, not pursued into the player_analyzeEnv* decl side (no decl type for it was in this track's scope).

10. Three ready-to-build decl mods

All three reuse only already-proven mechanisms: set for existing paths (tnc_waffe_explosivpistole pattern), file-replace where a path must be added (decl.set() raises DeclError("no such path") on anything not already in the text — confirmed by reading wolfsdk/decl.py directly, so new item[N] entries always need a full-text file, set never suffices for growing a list), and the resourceExec -s cvar route only where no decl lever exists at all.

10.1 "Bewegung & Überleben+" ("Movement & Survival+"; decl-only core + optional cvar layer)

Pure set edits, every path already exists in retail text, lowest risk:

{
  "id": "example.movement_plus",
  "spiel": "tnc",
  "assets": {
    "healthComponent:player/health_force_100": { "set": { "edit.max": 250, "edit.cur": 250 } },
    "healthComponent:player/armor_max_100":    { "set": { "edit.max": 300 } },
    "upgrade:player/perk/assassin_1":          { "set": { "edit.modifiersData.item[0].data.valueFloat": 4.0 } },
    "upgrade:player/perk/kobra_1":             { "set": { "edit.modifiersData.item[0].data.valueFloat": 2.2 } },
    "upgrade:player/perk/frontalassault_1":    { "set": { "edit.modifiersData.item[0].data.valueFloat": 4.0 } },
    "upgrade:player/perk/hardysoldier_1":      { "set": { "edit.modifiersData.item[0].data.valueFloat": 0.5 } }
  }
}

Effect: the moment each perk's (low) stat threshold is crossed once (assassin needs 10 stealth takedowns, kobra 1 constrictor takedown, frontalassault 10 takedowns, hardysoldier 20 overcharge kills — all _1, no grind to level 5), the player keeps max health/armor raised well past retail's 100/100 cap and gets stealth-crouch/prone speed, health regen rate and regen-delay all pushed past retail's own level-5 ceiling. Does not touch run/sprint/jump — those are cvars. Optional supplement, same mechanism tnc-console-unlock.md already proved for God/ g_infiniteAmmo, a loader-replaced cfile run with resourceExec <file> -s containing e.g. seta pm_sprintspeed "14" / seta pm_jumpheight "2.2" / seta pm_runspeed "9" — the mechanism is proven, this specific cvar set is not yet tested, flag it to the maintainer as such.

10.2 "Alle drei Kampfanzüge" (All Contraptions)

Zero new decl content needed — reuses r4_pickups.md's exact proven recipe, with a bonus this track found:

{
  "id": "example.all_contraptions",
  "spiel": "tnc",
  "assets": {
    "entities:<map>.world": {
      "set": { "world.edit.devmapInvLoadout": "devinvloadout/code_referenced/default_sp_devloadout" }
    }
  }
}

(<map>.world stands for the specific map's .entities world block — exact target path per r4_pickups.md's own proven c2v1 recipe, not re-derived here.) default_sp_devloadout already carries abilities/kobra+abilities/wanderer+abilities/rammbock together (§7.1) — one proven redirect delivers every weapon (already proven) and all three contraptions (new finding) at once. Optional UI polish, same resourceExec -s cvar route: seta journal_enable_contraption_tab_hack "1" (a real CHEAT-flagged cvar with a resolved callback, 0xf515b0) keeps the Journal's Contraption tab visible regardless of story progress.

10.3 "Waffen-Kits von Anfang an" ("Weapon kits from the start")

Two independent layers, both using only proven field paths:

(a) Immediate, decl-only — bump base ammo capacity directly (§6 table, exact same field tnc-decl-edits.md already confirmed in game):

{
  "id": "example.weapons_preupgraded",
  "spiel": "tnc",
  "assets": {
    "ammo:ammo/player/sp/smg":         { "set": { "edit.clipSize": 90 } },
    "ammo:ammo/player/sp/battlerifle": { "set": { "edit.clipSize": 60 } },
    "ammo:ammo/player/sp/shotgun":     { "set": { "edit.clipSize": 40 } },
    "ammo:ammo/player/sp/lkw":         { "set": { "edit.clipSize": 200 } },
    "ammo:ammo/player/sp/dkw":         { "set": { "edit.clipSize": 20 } }
  }
}

(b) Give upgrade-kit currency at spawn (needs a file-replace on the devInvLoadout decl because the path item[21] does not exist in retail text — hand-author the full decl: copy default_sp_devloadout's current 21 items verbatim, append item[21] = { item = "unlocks/upgradekit"; count = 20; forceCount = true; }, set startingInventory.num = 22). Hands the player 20 kits to spend at the first workbench on silencer/elephant-rounds/scope/etc. — the "intended" route, avoids relying on the unverified startActivated activation semantics from §6.

11. Open questions

  • Does giving the player all 3 abilities/{kobra,wanderer,rammbock} items at once (10.2) actually let all 3 sub-ability trees function, or does MAX_CONTRAPTIONS cap it to 2 silently? The stock devloadout already does this (§7.1) but nobody has watched it play.
  • Exact gating flag for a purchased weapon-upgrade-kit slot (upgradeNum/startActivated semantics, §6) — save-state, likely outside decl reach entirely; would need a save-file probe (re_probes/savegame/r4_savegame.md territory, different track).
  • armor_max_200's actual trigger condition (§3) — inferred from naming only.
  • Whether TNO's isDualWieldable also needs a maxCount=2 sibling edit like TNC's dualwieldable does (§8) — unconfirmed, TNO out of this track's depth budget.
  • Whether the per-weapon perk-upgrade modifiers (headshotter/triggerhappy/ secretagent/heavymetal, §5.4, corrected this pass) actually fire on every listed weapon in a live save, or only the first — the decl side is now resolved, the runtime side is still INFERRED (the "Upgrade %s Applyto %s" debug string is a lead, not proof).
  • What player/perk/inconspicuous_3/4/5's modifiersData.num=3 with only item[0] populated actually does at runtime (§5.4) — ignored, zero-read, or an authoring leftover; the only true num>populated-items gap found in all 195 upgrade decls.
  • Whether rewardScales/requiredStatForActivation.regions on the perk _base decls (§5.5) are purely UI/progress-bar cosmetics or feed any gameplay logic — named and resolved-string-checked this pass, behavior still unconfirmed; regions' one resolved bare-string reference is an ambiguous lead (§5.5), not disambiguated from an unrelated system.

Smallest in-game test (for the maintainer)

(Translated from German.)

  1. Movement+ (decl part): install mods/example_movement_plus (10.1, only the four upgrade + two healthComponent edits, without the cvar part), load a normal campaign map, do 10 stealth takedowns (triggers assassin_1) and check in the pause menu/HUD whether the maximum armor/health now shows above 100 (evidence for the healthComponent edit) and whether BJ moves noticeably faster while crouching (evidence for DMT_CROUCH_SPEED).
  2. All contraptions: on the already-proven dlc_0 custom-map route (c2v1), redirect devmapInvLoadout to devinvloadout/code_referenced/default_sp_devloadout (10.2), load the map, open the Journal → contraption tab: are Battle Walker, Ram Shackles AND Constrictor Harness listed as "owned" at the same time, or does the game force a choice/show only two? That answers the MAX_CONTRAPTIONS question directly.
  3. Weapon kits: the same map, pick up the SMG, read the magazine display in the HUD — does it show 90 instead of 45 (evidence for the ammo.clipSize edit from 10.3a, the same mechanism as the already-confirmed pistol magazine)?

Related: Research Report: R3 Newweapon, Research Report: R4 Pickups, Research Report: R4 Ai Decls, Research Report: R4 Savegame, Ghidra Analysis, TNO Modding Surface, TNC Modding Surface, TNC Console Unlock.