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:
- REFUTED, now fixed — pass 1's headline "genuinely new finding" and
§5.4/§5's "handgun only (N more orphaned)" cells for
headshotter,triggerhappy,secretagentwere wrong. Direct reads ofout/perks_all.txtshowheadshotter_N.edit.upgrades.num=6,triggerhappy_N.num=5,secretagent_N.num=2, everyitem[]a live, retail-wiredupgradedecl — none orphaned. Root cause: the ad-hoc script behind pass 1'sout/perk_table.txtread onlyitem[0]. Fixed with a new saved, re-runnablere_probes/playersys/build_tables.py(reads the fullitem[]array;--self-checkasserts the 6/5/2 counts so this can't silently regress) and a regeneratedout/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. - 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. - 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. - Followed up —
requiredStatForActivation.regionsand the UI/display fields (criteria/reward/regionUnlockName/rewardScales/presentationType/descriptionFlags) analyzed in new §5.5. Mostly the same INFERRED tier as the rest ofrequiredStatForActivation; one ambiguous lead (a bare"regions"string does carry a resolved reference, unlike every qualifiedrequiredStatForActivation.*string — flagged, not promoted to PROVEN, see §5.5) and one new confirmation (presentationType_t/perkDescriptionFlags_texist as real mangled RTTI enum-type names). - 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 ownapplyTofield semantics. See §5.4. - 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, nogameModeFlagsfield 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=999oncampaignneeds afilereplace (field absent from that decl's own text,setcannot 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'sAMT_AMMO_MAX_COUNTperk levels, flatsetedits.weaponUpgrade/laserCutterUpgrade(17 decls) — TNO's equivalent of TNC's per-weaponupgrade:weapons/*kits;weaponToUpgradecan be re-pointed at a different weapon decl (a "mod transplant" exactly like TNC'svalueDeclswap technique).- Dual wield:
docs/modding.mdalready shows the lever,edit.isDualWieldable=trueon anyweapon:*decl (TNO's field name differs from TNC'sdualwieldable, otherwise the same idea) — no per-weaponmaxCount=2requirement 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.clipSizearrives in game (handgun 10→30,tnc-decl-edits.md, user-confirmed screenshot).resourceExec <loader-replaced cfile> -sexecutes non-exposed commands/cvars (tnc-console-unlock.md) — the mechanism this report's cvar-based Movement+ layer depends on.dualwieldable=true+maxCount=2is 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'smodifiersData/modifiersWeapon/modifiersAmmostructure field-for-field (this track,--stringson 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). healthOverrideshas a resolved.rdatareference (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_devloadoutalready contains all 3 contraption ability items simultaneously, in retail, unmodified.- This pass:
headshotter/triggerhappy/secretagent/heavymetaleach buff all of their listed weapons (6/5/2/4), not just one — direct corpus read, regression-checked bybuild_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_texist 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/lockedFromStartare read exactly as named (exact compiled strings, no resolved xref this run).armor_max_200is whatAT_ARMORBOOSTswaps in (no explicit numeric link found in any decl; inferred from naming + the ability's existence).startActivated=trueon 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_CONTRAPTIONScaps 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 theapplyTodecl field's own semantics is new circumstantial support (§5.4). - This pass:
requiredStatForActivation.regionsand the perk_baseUI fields (rewardScales,criteria,reward,regionUnlockName) are read as named — same tier asrequiredStatForActivationitself; 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/lockedFromStartto resolve a.rdataxref the same wayhealthOverridesdid (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 unlabeledidAnimSysNode_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, givenleancontrols exist per the task's movement list) — none exists; lean is driven by theplayer_analyzeEnv*cover/peek system, not a free, tunable lean amount. Confirmed dead end (exhaustivepm_/player_substring filter over all 3256 cvars), recorded, not pursued into theplayer_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 doesMAX_CONTRAPTIONScap 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/startActivatedsemantics, §6) — save-state, likely outside decl reach entirely; would need a save-file probe (re_probes/savegame/r4_savegame.mdterritory, different track). armor_max_200's actual trigger condition (§3) — inferred from naming only.- Whether TNO's
isDualWieldablealso needs amaxCount=2sibling edit like TNC'sdualwieldabledoes (§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'smodifiersData.num=3with onlyitem[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 195upgradedecls. - Whether
rewardScales/requiredStatForActivation.regionson the perk_basedecls (§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.)
- Movement+ (decl part): install
mods/example_movement_plus(10.1, only the fourupgrade+ twohealthComponentedits, without the cvar part), load a normal campaign map, do 10 stealth takedowns (triggersassassin_1) and check in the pause menu/HUD whether the maximum armor/health now shows above 100 (evidence for thehealthComponentedit) and whether BJ moves noticeably faster while crouching (evidence forDMT_CROUCH_SPEED). - All contraptions: on the already-proven
dlc_0custom-map route (c2v1), redirectdevmapInvLoadouttodevinvloadout/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 theMAX_CONTRAPTIONSquestion directly. - 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.clipSizeedit 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.