Wolfenstein Modloader Buy me a coffeeKaffee spendieren

Wiki / Latest research roundsNeueste Forschungsrunden

TNO Decl System: Catalogue, Loader Capability, New Entries, Load-Order Traps

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

In plain wordsIn einfachen Worten

This report catalogues the text definitions of Wolfenstein: The New Order (TNO) for weapons, ammo, damage, enemies and the player. They use the same format and patch method as Wolfenstein II, but TNO has one flat set of 14 archives with no DLC or patch layer, and the loader patches every copy, so the DLC folder trap from Wolfenstein II cannot happen. A real difference: 252 of 255 enemy definitions set their own health, so one shared edit cannot reach all enemies, while the aim spread of 26 of 29 enemy weapons can be changed with just 10 edits. Everything is static: the patcher applied 8 installed mods cleanly to the real archives, but the game was not launched and none of the three mod designs was tried.

  • 'set' edits can only change values that already exist; adding a new field, as the aimAssistRange mod does, needs a full file replacement.
  • 252 of 255 enemy definitions set their own health, so a health mod needs a generator over all 252 entries; the TNC-style single shared edit fails here.
  • Editing spread in 10 enemy weapon definitions reaches 26 of 29 through inheritance (correction: pass 1 said 10); whether spread really controls hits is not confirmed.
  • Adding brand-new entries to a TNO archive is still open with no tool yet; the end of each entry was decoded as 9 zero bytes plus an ID unique per chunk and type for definition types.
  • Static only: 8 installed mods were patched cleanly (377 assets), but the three designs (health slider, full arsenal from chapter 1, accuracy slider) are untested in game.

Dieser Bericht katalogisiert die Text-Definitionen von Wolfenstein: The New Order (TNO) für Waffen, Munition, Schaden, Gegner und Spieler. Sie nutzen dasselbe Format und dieselbe Patch-Methode wie Wolfenstein II, aber TNO hat nur einen flachen Satz aus 14 Archiven ohne DLC- oder Patch-Ebene, und der Loader patcht jede Kopie, sodass die DLC-Ordner-Falle aus Wolfenstein II nicht auftreten kann. Ein echter Unterschied: 252 von 255 Gegnerdefinitionen setzen ihre Gesundheit selbst, eine einzige gemeinsame Änderung erreicht also nicht alle Gegner, während sich die Zielstreuung von 26 der 29 Gegnerwaffen mit nur 10 Änderungen anpassen lässt. Alles ist statisch: Der Patcher hat 8 installierte Mods sauber auf die echten Archive angewendet, aber das Spiel wurde nicht gestartet und keiner der drei Mod-Entwürfe ausprobiert.

  • „set“-Änderungen können nur vorhandene Werte ändern; ein neues Feld hinzuzufügen, wie es die aimAssistRange-Mod tut, braucht einen kompletten Dateiersatz.
  • 252 von 255 Gegnerdefinitionen setzen ihre Gesundheit selbst, eine Gesundheits-Mod braucht also einen Generator über alle 252 Einträge; die TNC-artige einzelne gemeinsame Änderung scheitert hier.
  • Eine Änderung der Streuung in 10 Gegnerwaffen-Definitionen erreicht durch Vererbung 26 von 29 (Korrektur: Durchgang 1 nannte 10); ob die Streuung wirklich die Treffer steuert, ist nicht bestätigt.
  • Ganz neue Einträge in ein TNO-Archiv einzufügen ist noch offen, ein Werkzeug dafür fehlt; das Ende jedes Eintrags wurde als 9 Null-Bytes plus eine ID entschlüsselt, die für Definitionstypen pro Chunk und Typ eindeutig ist.
  • Nur statisch: 8 installierte Mods wurden sauber gepatcht (377 Assets), aber die drei Entwürfe (Gesundheitsregler, volles Arsenal ab Kapitel 1, Treffgenauigkeitsregler) sind im Spiel ungetestet.

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 The New Order's decl system: a field catalogue for weapon, ammo, damage, AI and player decls, what the loader can already change, whether brand-new (type, name) entries can be added to a TNO archive, and whether TNO has TNC-style load-order traps. TNO uses the same decl dialect and patch mechanics as TNC but a simpler single-tier archive, and the loader patches every duplicate copy, so the TNC dlc_0 precedence trap does not exist; instead, almost every AI decl overrides health individually. Three TNO mod designs are included. Static only; no game was launched. The append-entry writer this round could not build follows in Adding new entries to TNO archives (R7).

Track tnodecls, round 6, 2026-10-01. Scope: TNO only (<Steam library>\steamapps\common\Wolfenstein.The.New.Order, id Tech 5, WolfNewOrder_x64.exe, 14 chunkN.index/chunkN.resources pairs via master.index, no DLC/patch tier at all). Read-only against the game install and every project file except this track's own folder re_probes/tnodecls/ and this report. The game was not launched, no debugger attached, no process touched. TNO has no runtime image (unlike TNC's re_probes/runtime_87400/module.bin + tools/decomp.py) — every claim here about what the engine does with a decl field comes from the shipped decl text itself, from docs/*.md's own prior binary-string findings (never re-derived), or from this project's own patcher code (wolfsdk/resources.py, wolfsdk/patch.py, wolfsdk/decl.py) and the archive bytes it reads/writes. No disassembly was done this round; where a TNC report's method (tracing a field into .text) would have settled a question, it is flagged as not settleable without a TNO runtime image, not silently skipped.

Built on, not redone: docs/moddingflaeche-tno.md (full corpus census, 105 types, 25 559 parsed text decls, the gamemodesp/faction/enigma cheat routes, startingInventory, 20 largest unused types) — treated as ground truth and cited, not re-measured, except where I independently re-verified a specific field against the pristine backup (see §0). The existing TNO mods in mods/ (cheats_gamemode, enigma_frei, fraktionen_hass, supersoldaten_c15, zielhilfe_alle, wucht_treffer, wucht_waffen, menue_marke, widerstand, ragdoll_gore, gore_*, example_*) were read as proof of the loader's capability, not re-explained. docs/formats.md/docs/limits.md (archive format, the ascending-offset invariant). docs/ladepfade.md Teil 1 (TNO's fs_overlaypath/loose-file findings — relevant to §3). wolfsdk/cvars.py (the 25 measured RETAIL_BLOCKED TNO cvars — why the decl route is needed at all). TNC's r2_newassets.md/r3_newassetsv.md (their append-entry technique for id Tech 6's different IDCL format) are cited only as a contrast in §3 — their method does not transfer; TNO's chunkN.index is a structurally different, simpler format.

0. A methodology note that changed several readings

D:\...\Wolfenstein.The.New.Order\base\ is not pristine — a prior session already ran apply() with 8 TNO mods (gore_tief, gore_ultimativ, enigma_frei, ragdoll_gore, gore_realistisch, zielhilfe_alle, gore_texturen, widerstand; _wolfsdk_backup/journal.json: "missing": [], "state": "applied", 377 assets, all 14 chunks rebuilt, 0 in-place slots). The pristine originals live in base/_wolfsdk_backup/. Every decl quoted below was re-read from _wolfsdk_backup, diffed against the live copy, and the diff is reported where it mattered — twice this caught my own first read quoting an already-modded value (weapon/handgun_base's clipSize, projectile/handgun's aimAssistRange) as if it were retail. re_probes/tnodecls/pristine_*.decl holds the 7 confirmed-pristine extracts this report draws field names from.

python re_probes/tnodecls/probe_tail.py   # §3's tail-byte measurement, read-only

Answer in one paragraph

TNO's decl system is the same dialect and the same patcher mechanics as TNC (confirmed directly: aiHealth.components.components[0].max/cur, actorEditable.damageGroups with 13 zones, faction.myFaction, startingInventory.item[].{startSlot,inventoryDecl} all exist byte-for-byte under the same paths in TNO's entityDef:ai/enemies/default and entityDef:player/game/wolf/*), but the archive container is a different, simpler, single-tier format (chunkN.index, no DLC/patch layering) and the loader already patches every one of the 14 chunks for every duplicate copy of an asset (Installation.find()/apply(), confirmed by code read, §2), which removes the TNC-style "wrong archive wins" trap entirely — there is no equivalent of TNC's dlc_0 precedence to get wrong, because TNO has no DLC/patch tier to begin with (§4). What TNO does have, and TNC's corpus doesn't share, is a structural AI trap: where TNC can flip HP for 125 enemies by editing one shared root decl (soldier_base), TNO's AI decls override aiHealth individually in 252 of 255 corpus entries (measured, §1.4) — there is no cheap single-point health lever, only a generator-script lever. The loader's set mechanism can only touch paths that already exist in a decl (wolfsdk/decl.py:apply_patch raises DeclError on an unknown path) — inserting a genuinely new field (as zielhilfe_alle does for aimAssistRange, confirmed absent from the pristine projectile/handgun, §0) requires a full "file" replacement, not set. Adding a brand-new (type, name) entry to a TNO archive has no code path in this project (resources.py's rebuild() explicitly rejects any payload whose key matches no existing entry, §3) and was never attempted here — but the format itself looks more tractable than TNC's IDCL: no header hash chain, no checksum anywhere, and this round's own measurement (§3, re-verified across all 14 chunks in round 2 after the verifier caught an overgeneralized version of this claim) confirms docs/formats.md's guess that the mysterious 13-byte entry tail is 9 zero bytes + a BE u32 ID, unique per chunk per type for decl-ish types (not globally, and not for audio/particle types) — a concrete, previously-unverified fact, but turning it into a working "append a new entry" writer, round-tripped through the game itself, is out of this round's scope (no runtime image to validate against, and the task's time budget does not cover building and testing a second archive-format writer from scratch). Three ready-to-build designs follow in §5, each scoped to mechanisms already proven safe by the existing mods: a corpus-wide AI-health scaler (252 set edits, generator script), a "full arsenal from chapter 1" loadout swap (one file replace, using retail's own late-game array as the exact donor text — zero guessing), and a 10-decl AI-accuracy slider (set on accuracySettings[].spread, the one true single-point AI combat lever this round found).

Pass-1 errors (fixed in round 2)

The verifier ran an independent re-check and found three real defects in pass 1. All three are now re-verified with broader, fresh measurements (all 14 chunks, not just chunk1/chunk1.index's 40-sample spot check); details are inline at each affected section (§1.1, §3), and the corrected PROVEN/INFERRED lists below reflect the fixes. Short version:

  1. §3's tail-id claim was overgeneralized. Pass 1 said "the 13-byte entry tail is 9 zero bytes + a BE u32 id that increments by exactly 1 between successive same-type entries" as a blanket fact, checked on 40 consecutive entityDef entries in one chunk. Fixed: re-measured across all 14 chunks, every type, with re_probes/tnodecls/probe_tail_v2.py through _v5.py. The claim holds — but only per chunk, and only for decl-ish types (entityDef, weapon, damage, ammo, projectile, aiBehavior, material, trackingParms, faction, gamemodesp, soundmixing: zero within-chunk id collisions in all 14 chunks, every type). It is false for sound/particle/renderProg/cuttableResource (real same-chunk, same-id duplicates — e.g. 50 sound entries all named weapons/smg/ai/smg_ai_fire_nearmid sharing id 111 in chunk1), and the verifier's own counter-claim ("ZERO duplicates… even stronger than the report's own check") was also wrong as a blanket statement — it silently combined ids across all 14 chunks, where the same id value legitimately recurs for unrelated content in different chunks (the id's real scope is per-chunk, not global — see §3). The verifier's specific counter-examples (sound, particle, renderProg, cuttableResource showing duplication) are independently confirmed correct. cm is clean in 11 of 14 chunks and near-clean in the rest (2 dup entries out of 983 in one chunk) — closer to the decl-type behaviour than to sound's.
  2. §1.1's negative control didn't check inherit =. Pass 1 classified the 19 weapon/ai/* decls lacking their own accuracySettings[].spread as "melee/static, no spread concept" by presence/absence in the decl's own text only. Fixed: re_probes/tnodecls/probe_weaponai_inherit.py resolves every one of the 19 decls' inherit = chains. 16 of 19 cascade to one of the 10 spread-bearing base decls (e.g. submachinegun1946 → assaultrifle1960, pistol1946 → pistol, shotgun_lefthand → shotgun); only weapon/ai/baton and weapon/ai/fists/supersoldat1946 are genuinely melee with no inherit; weapon/ai/mg46/supersoldat1946 is a real, unexplained standalone exception (60 own fields, no inherit, no spread). §5.3's actual reach is 26 of 29 weapon/ai/* decls, not 10 — corrected below.
  3. Minor: §5.2 described c01p1's pristine knife as plain weapon/knife; the real value is weapon/knife46cutonly (confirmed by direct re-read). Does not change the recipe (a full-block replace), only the description — fixed below.

New this round, not in the verifier's list (answering its "missed" items and pushing the open questions further): the unexplained 154 probe_tail.py mismatches are now explained (narrowing the search window from 64 to 256 bytes drops the all-14-chunks total from 30 506 to 2 793, and 100% of what's left is type sound — the window was simply too narrow, exactly as the verifier suspected, §3); and docs/formats.md's open "Zähler A" ("counter A") question is now substantially resolved, not just left open: counter A equals the number of distinct (type,name) pairs in that chunk, confirmed exact in 12 of 14 chunks and off by ≤16 (<0.1%) in the other 2 (§3).

1. Decl field catalogue: weapon, ammo, damage, AI, player

All field names below are read directly from _wolfsdk_backup (pristine) unless marked "live (modded)". python -m wolfsdk --title tno cat <type>:<name> reproduces every one of these reads against the live install; re_probes/tnodecls/pristine_*.decl holds the pristine bytes.

1.1 weapon — weapon/handgun_base (4 577 B, pristine)

Confirms and extends moddingflaeche-tno.md's "already used" entry with the full field surface (not previously dumped in any doc):

killStat, allowOneHandedReload, lowerWhenNoTension, isDualWieldable,
internalName, pickupName/pickupNameMulti, handsFovScale, quickSlotGroup,
giveable, dropable, numHands, sellPrice, itemUse, weaponFX,
silencedGunfireEventDecl, gunfireEventDecl, initialAmmoDecl,
validAmmoClips.item[0].{validAmmoDecl, clipSize}        # clipSize = 20 pristine
initialBurstMode, burstCounts.burstCounts[0..2],
firingIntervals.firingIntervals[0..2]                   # [150, 80, -1] pristine
ammoPerShot, weaponSlot, weaponSelectionPrio, selectionSlot,
maxRange, maxOptimalRange, reloadInterval,
handLag.{clamp,scale,retain,recenter,offsetAtMaxPitch,offsetAtMinPitch,
         offsetByPitch,offsetByYaw},
handRotation.{minAngles,maxAngles,scale,retain,accelMaxDipAngle,
              accelRotChangeSpeed,strafeRightTiltAngle,strafeLeftTiltAngle},
handCoverMovRot.* (12 offset/rot sub-blocks for cover poses),
weaponIdleRotation, dwWeaponModeOffset(Sec), weaponOffset(Sec),
dwWeaponModeRollAngle(Sec), weaponRollAngle(Sec),
weaponAnimSet, pistolWhipProjectile, lockTime, zoomMode,
ironSightZoom.{zoomedFOV, mouse/joy_sensitivity_scale, zoomTime,
               spreadReductionWithZoom, scopeGuiNum},
weaponDof/weaponZoomDof.{focus,nearDist,scale},
movementScale.{stand_zoom_scale,crouch_zoom_scale,stand_dw_scale,crouch_dw_scale},
spreadReductionWithCrouch, spreadIncreasedByMovement,       # 0.5 pristine
introAnimationID, shellEmitter.{breakableEmitter,baseSpeed,deltaSpeed,deltaAngle},
shellEjectLeftTagName/FirstPersonOffset, hideShellsOnZoom,
allowSprintFire(Sec), allowSprintDW, twoHandsswitchAnim,
meshesHiddenOnPrimFiremode.item[N]

widerstand's live diff against this pristine (confirmed by difflib.unified_diff, 3 hunks only): clipSize 20→30, firingIntervals[0] 150→90, firingIntervals[1] 80→55, spreadIncreasedByMovement 0.5→0.2 — exactly the fields the mod's own description claims, independently re-verified against the real archive bytes, not just trusted from the mod's description string.

AI-side weapon, weapon/ai/assaultrifle1960 (pristine, unaffected by any installed mod): same top-level shape, but with the field this round's §5.3 design needs:

accuracySettings.accuracySettings[0..5].spread = 100, 75, 60, 50, 40, 25
  # indices = ACCURACY_HORRIBLE..ACCURACY_PERFECT, same 6-tier enum TNC uses
accuracySettings[N].wanderingAim.aimCorrectionDuration = 1000 (every tier)

Measured corpus-wide (re_probes/tnodecls/probe_weaponai_inherit.py, re-runnable, all 14 chunks): 29 distinct weapon/ai/* decls, of which 10 carry their own accuracySettings[].spread (the ranged ones — shotgun, assaultrifle1960, pistol, drone, robot, mg60/supersoldat1960, wol000d_pistol, assaultrifle1960rocket, mechadeathshead_laser, prototyperobot_lkw); spread values range 15.0 (wol000d_pistol, flat across all 6 tiers — a boss/unique) to 160.0 (shotgun's worst tier).

Round-2 fix (verifier-confirmed defect, see "Pass-1 errors" above): pass 1 classified the 19 decls without their own accuracySettings[].spread as a clean negative control ("melee/static, no spread concept") by checking only each decl's own text, the same inheritance blind spot this report elsewhere warns about for TNC's AI-health lever (§1.4). Re-checked this round by resolving every one of the 19 decls' inherit = target chain (probe_weaponai_inherit.py): 16 of the 19 transitively inherit a working accuracySettings[].spread from one of the 10 base decls — assaultrifle1946/assaultrifle1960_bakedreverb/submachinegun1946(_nh)/ dummy/submachinegun1946(_noflash) → assaultrifle1960; pistol1946/pistol_white → pistol; shotgun_lefthand/shotgun_shrapnel(_lefthand) → shotgun; heavy_robot_laser(_strong)/robot_c03p2/robot_temp → robot; mechadeathshead_laser_stage3 → mechadeathshead_laser. Only weapon/ai/baton and weapon/ai/fists/supersoldat1946 are genuinely melee with no inherit and no spread concept — the negative control pass 1 claimed. weapon/ai/mg46/supersoldat1946 is a real, unexplained exception: standalone (no inherit), 60 own fields, genuinely no spread field of any kind — not resolved this round, a real open item, not a missed inherit edge. Net effect, and it's a better result for §5.3 than pass 1 claimed: editing the 10 base decls' spread cascades to 26 of 29 weapon/ai/* decls corpus-wide (not just the 10 edited directly) — corrected in §5.3 below.

1.2 damage — damage/tungsten/mg46 (372 B pristine vs 461 B live)

damageParms.{damageName, damageTypes, goreTypes, declGoreBehavior,
             minDamage, maxDamage, ragdollImpulseMag}

Pristine: minDamage=10, maxDamage=15, ragdollImpulseMag=500, no cuttableRadius/ragdollOnDeath key at all. Live (both widerstand and ragdoll_gore touch this same decl, confirmed from both mods' assets lists): minDamage=22, maxDamage=30, ragdollImpulseMag=1100, cuttableRadius=14.0, ragdollOnDeath=true — i.e. ragdollOnDeath and cuttableRadius were inserted as new keys, which per §2.3 means that specific edit must have shipped as a "file" replacement for this decl (not set), consistent with ragdoll_gore's own stated technique. Two independent mods editing the same decl applied cleanly in one apply() run (journal shows 0 "missing", state: applied) — confirms the loader resolves multi-mod collisions on one asset without erroring (last-applied-wins by mod priority, not re-derived here, already documented behaviour of mod.build_payloads).

1.3 ammo — ammo/handgun (401 B)

inherit = "ammo/light";
edit = { projectileDecl, lowAmmoInClip, lowAmmoInPocket,
         minRandomCount, maxRandomCount, icon, icons.icons[0..2] }

Matches moddingflaeche-tno.md §2.15's ammo/rocketlauncher field set (same type, this is a second, independently-read instance: lowAmmoInClip, lowAmmoInPocket, minRandomCount/maxRandomCount confirmed present on both). No maxCount/count/giveable on this particular decl — those are present on other ammo/* entries per the prior doc, so the schema is a superset, not every decl using every field (expected for a decl format with optional keys).

1.4 AI — entityDef:ai/enemies/default (5 079 B pristine) + corpus census

Full structure (same shape as TNC's idAI2 decls, confirmed field-for-field, see also r4_aidecl.md for the TNC side — not re-derived, independently re-confirmed to exist in TNO too):

class = "idAI2"; inherit = "ai/default";
edit.renderModelInfo / clipModelInfo / dormancy / afProperties
edit.actorConstants.{perception, footstepEffectTables, waterInteraction}
edit.faction.myFaction = "faction/auth"
edit.mass
edit.aiConstants.{physics, movement, animation}
edit.aiEditable.{perception, sounds, behaviors, death, interactions,
                  rollBonesDecl}
edit.aiHealth.components.components[0] = { max = 50; cur = 50; }
edit.aiHealth.components.components[1] = { max = 0;  cur = 0; }   # dead 2nd slot, same as TNC's r4_aidecl finding
edit.fxDecl
edit.actorEditable.damageGroups.item[0..12]   # 13 zones, same groupName/tagName/fxTagID set as TNC
edit.actorEditable.radiusDamageJoints.item[0..5]
edit.factionType = "FACTIONTYPE_AUTHORITY"
edit.takedownTypes.{stealthSWFWD, stealthSWREV, stealthDWFWD, stealthDWREV,
                     stunnedSWFWD, stunnedSWREV, groundFront, groundRear,
                     AITakedowns}
edit.walkIKDecl; edit.targetLockJoints.item[0..5]

Corpus census (entityDef type, name starts ai/, all 14 pristine chunks, de-duplicated by name): 255 distinct ai/* entityDefs, 444 raw entries (1.74× duplication across chunks on average — confirming the loader's "patch every copy" design in §2 is load-bearing, not theoretical). Of the 255, 252 carry their own aiHealth.components.components[0] override; only 3 do not (ai/groupmanager, ai/scriptedanimweb, ai/tungsten/wol000d/guard_spriteanimator — all three non-combat/utility, not health-bearing bodies). This is the key structural difference from TNC:

FAILED HYPOTHESIS: "Editing ai/enemies/default's aiHealth will cascade to every inheriting enemy, the same lever r4_aidecl.md found for TNC's soldier_base (125/185 combat archetypes inherit it without their own override)." Refuted by direct corpus measurement: 252/255 TNO ai/* decls set their own aiHealth, so an edit to the root changes at most 3 non-combat entries. TNO's AI health is authored per-leaf, not inherited — a corpus-wide health mod needs a generator over all 252 decls (exactly what §5.1 below builds), not a one-line edit to a shared root.

aiBehavior:behaviors/enemies/default (1 293 B, pristine, unmodified by any installed mod — confirmed identical live/pristine):

traversalAttackTolerance, canUseTraversals, usesSearch, usesAvoid,
damageBehaviors.{longPainDamageFilter, hasDeathAnims},
friendlyAttitudeAdjust, itemSelect.itemSelect[N],
canGreetPlayers, investigatesBody,
takeCoverBehaviors.{maxWaitTimeAfterReachingCover, minWaitInCoverTime,
                     maxWaitInCoverTime}.setting.setting[N],
has157Turns, min/maxCloseMeleeInterval, min/maxChargeInterval,
min/maxChargeDistance,
dodging.{tooCloseDist, aimHeavyCoverHideChance, damageHideBailChance,
         damageHideBailMaxChance, minAimDodgeInterval, maxAimDodgeInterval},
min/maxThrowInterval

trackingParms:trackingparms/aim_default (referenced by name from aiEditable.behaviors.aimTrackingParms on every AI that doesn't override it — same indirection pattern TNC's r4_aidecl.md found): parms.item[0..6] = { kickScale, rate.value, acceleration.value, deceleration.value, springMaxLength } — this is tracking-speed/smoothing, not a hit/miss probability field (no aimError key exists in TNO's version of this decl, unlike the TNC decl r4_aidecl.md describes under the same type name — a real, measured difference between the two games' use of the same decl type, not a mistake in either report). The actual accuracy lever in TNO is the weapon-side accuracySettings[].spread shown in §1.1, confirmed by direct read, not inferred from TNC's shape.

1.5 Player — entityDef:player/game/wolf/chapter_start_default (28 459 B, pristine, unmodified)

edit.clipModelInfo / fxDecl / afProperties
edit.actorConstants.{alwaysUsePlayerClip, perception, actorSounds,
                      footstepEffectTables, footstepEvents}
edit.faction.myFaction = "faction/player"
edit.startingInventory = { num = 1; item[0] = { startSlot = "EQUIPPED";
                                                 inventoryDecl = "weapon/knife"; } }
edit.walkIKDecl; edit.lightDebugHelperTypes.item[0..4]
edit.thirdPersonHandsDef/LegsDef, handsModel, handsAnimWebName(+Secondary/Additive)
edit.playerProps = "player/default"
edit.standDecay / crouchDecay = { time, linear, lambda }
edit.playerHealth.components.components[1] = { cur = 100; }   # index 1, not 0 -- differs from TNC's aiHealth[0] convention, confirmed by direct read
edit.prtUnderwater; edit.maxTimeUnderwater.value = 50000;
edit.maxFreeTimeUnderwater.value = 10000; edit.sprintAirReductionScale = 1
edit.suffocationDamage / suffocationDamageLong /
     suffocationStep1/2/3Sound / suffocationDeathSound
edit.grappleTypes.item[0..2]
edit.perkAbilities.perkAbilities[0..36]   # name/desc/icon/icon_locked/picture/ablilityModifier [sic, shipped typo]
edit.perks.perks[0..31]   # perkName/perkDesc/countRequired/countDesc/
                           # additionalCount/addCountDesc/additionalRequirement/addReqDesc/
                           # requiredPerks.item[]/unlockedAbilities.item[]
edit.upgradeInfo.upgradeInfo[N] = { is46, foundOnMap, allowedOnMap,
                                     linkUpgrade46, linkUpgrade60 }

example_dev_loadout's target (playerHealth.components.components[1].cur) is confirmed real and exactly as moddingflaeche-tno.md §4.4 already reported — independently re-read here, not re-derived.

30 player/game/wolf/* entityDefs exist corpus-wide (one per chapter-part, plus chapter_common_default/chapter_part_default/ chapter_start_default). Every chapter-part decl carries its own startingInventory (never just inherits the 1-item default) — read directly for c11p1 (5 items incl. weaponupgrades/shotgun_shrapnel), c13p1 (2 items), c14p1 (4 items incl. weapon/laserkraftwerk equipped), c15 (6 items — the richest found: knife, laserkraftwerk, assaultrifle1960 equipped, shotgun, ar60marksman, handgun), c16p2 (num = 0 — an endgame chapter that intentionally starts the player with nothing, presumably items are granted mid-level by script instead). This is the exact real-retail donor array §5.2 uses verbatim.

2. What the loader can already change — proven by the installed mods

Not re-explained (each mod's own description in mods/*/mod.json already states its mechanism); what's new here is independently confirming, via _wolfsdk_backup/journal.json, that all 8 currently-enabled TNO mods applied cleanly against the real archives: "missing": [], "state": "applied", 377 distinct (type,name) assets written, all 14 chunks rebuilt (0 fit in-place — every touched chunk needed a full Archive.rebuild(), not write_in_place(); expected when several mods touch many small decls scattered across a chunk, each individually too large or too small to fit its own slot via deflate_exact's padding search). This is end-to-end proof the patcher mechanism (not the in-game effect, which the harness's own RUNTIME-PROVEN section already asserts generically for TNO) works against the live archive set, not just in isolated test fixtures.

Decl types the loader already touches in a shipped TNO mod, with mechanism:

Type Mod Mechanism Field(s)
gamemodesp cheats_gamemode file (field absent in campaign) startingHealth, gameModeFlags
enigma enigma_frei set (8 surgical edits) requiredSecrets.num = 0
faction fraktionen_hass file (new relationships block) relationships.relationships.item[N]
file:*.entities supersoldaten_c15 file, inherit-target string swap only one entityDef block's inherit
projectile zielhilfe_alle file (field inserted, absent in all 23 pristine targets) aimAssistRange
soundmixing wucht_waffen set two mix-bus gain values
sound wucht_treffer set/file burst-impact count, random-volume range
file:strings/english.lang menue_marke set, byte-length-exact (raw-stored type) 3 UI string lines
weapon example_big_magazine, example_fast_shotgun, widerstand set clipSize, firingIntervals[N], spreadIncreasedByMovement
entityDef (player) example_dev_loadout set playerHealth.components.components[1].cur, maxTimeUnderwater.value
damage example_weapon_tuning, widerstand, ragdoll_gore set + file (new keys) minDamage/maxDamage, ragdollOnDeath/cuttableRadius/ragdollImpulseMag
material example_white_handgun, widerstand set diffusemap, usevirtualmapping redirect
entityDef (AI) widerstand set per-chapter AI startingInventory/health overrides

New, not previously documented anywhere: set on an existing path and file (full replacement) are not interchangeable — wolfsdk/decl.py:apply_patch() raises DeclError("unknown path(s): …") the moment a patch dict names a path absent from the target decl's own parsed Decl.values. Every mod above that adds a field (zielhilfe_alle's aimAssistRange, ragdoll_gore's ragdollOnDeath/ cuttableRadius, fraktionen_hass's relationships block, cheats_gamemode's whole flag set) must ship as "file", confirmed by reading their mod.json ({"file": "assets/…"}, not {"set": {...}}). Every mod that only changes a number already present uses set. This is the exact mechanical boundary a mod author needs to know and was not stated this precisely in any prior doc.

3. Can NEW (type, name) entries be added to a TNO archive?

No code path exists in this project for it (PROVEN, by code read):

  • wolfsdk/resources.py:Archive.rebuild() builds its payload set only from self.entries (the entries the pristine index already parsed) and explicitly raises ResourceError if any payload key matches no existing entry (missed = set(payloads) - applied, §"rebuild", lines 288–298). There is no insert/append function anywhere in resources.py or patch.py.
  • ResourceIndex is held as its original byte buffer (self.buf = bytearray(buf)) and only ever mutated via set_sizes(), which overwrites 12 bytes at an existing entry's known pos — there is no code that grows the buffer or inserts a new trio + size-fields run.
  • Appending data to the end of a .resources file while leaving the index's entry order unchanged is the exact failure mode docs/formats.md/docs/limits.md already document as measured in a real game launch: FATAL ERROR: idBackgroundLoader::BeginBackgroundLoads: FileTable is out of order. A new entry row placed at the very end of the entry list, pointing at data appended at the very end of the .resources file, would not trigger that specific failure (offsets would still ascend) — but this is reasoning from the invariant's stated cause, not a tested claim, and I did not build or test it.

What this round newly measured, not just inferred (PROVEN, by direct byte read, round 2 widened from 1 chunk/40 samples to all 14 pristine chunks, every type: re_probes/tnodecls/probe_tail_v2.py through _v5.py, re-runnable):

entries (chunk1 alone): 4241   header counters @0x20/0x24: (3508, 4241)
tail length distribution (all 14 chunks): 13 bytes dominates for
  entityDef/weapon/damage/ammo/projectile/aiBehavior/material/trackingParms/
  faction/gamemodesp/soundmixing/cm; "sample" is mostly 37/109 bytes; "sound"
  is mostly 13 bytes but has rarer 133/205-byte tails too (heterogeneous).

docs/formats.md's own hedge — "meist 13 Bytes (9 Nullen plus eine fortlaufende BE-u32-ID)" ("mostly 13 bytes (9 zeros plus a running BE u32 ID)") — is confirmed, with a corrected scope (this is the round-2 fix for the verifier's first finding, see "Pass-1 errors" above): 9 zero bytes, then a BE u32 that behaves as a per-(chunk,type) counter, not a global one:

  • Unique within one chunk, per type, for every decl-ish type checked (entityDef, weapon, damage, ammo, projectile, aiBehavior, material, trackingParms, faction, gamemodesp, soundmixing): 0 within-chunk id collisions in any of the 14 chunks, for any of these types (probe_tail_v3.py). cm is clean in 11/14 chunks and near-clean in the other 3 (at most 2 duplicate entries out of 983 in the worst chunk) — closer to decl-type behaviour.
  • NOT unique across chunks, and this is expected, not an error: the same id value recurs for completely unrelated (type,name) pairs in different chunks (e.g. entityDef id 12189 is ai/tungsten/c03/part_02/soldier_07 in one chunk and breakable/environment/1946_crate_pieces in another; probe_tail_v4.py). Symmetrically, the same name gets a different id in each chunk it has a copy in (e.g. weapon/ai/submachinegun1946: ids {3491, 14445, 17814} across its 3 chunk copies). This answers pass 1's own open question ("unique only within one chunk, or across all 14?"): within one chunk only — a new entry's id only needs to avoid collision with existing ids of its own type in the one chunk being edited, not across the whole 14-chunk install. This also means the verifier's own "independent re-check" claiming "ZERO duplicate ids across all 14 chunks" for these same types was itself wrong as stated — it silently combined ids across chunks, which was bound to show "duplicates" under the real per-chunk-scoped counter; see "Pass-1 errors" above.
  • NOT unique even within one chunk for sound/particle/renderProg/ cuttableResource — confirmed real, e.g. in chunk1 id 111 is shared by 50 sound entries all named weapons/smg/ai/smg_ai_fire_nearmid (probe_tail_v5.py), and chunk0's renderProg has 421 of 846 entries in duplicate-id pairs. These are plausibly variant/take indices into a shared audio-cue or shader-permutation table rather than per-entry unique ids — not traced further, INFERRED only, same caveat as below.

It is not a global file-position counter either way (it jumps discontinuously between type boundaries within a chunk), consistent with a per-(chunk,type) sequential id assigned at content-build time for decl-ish types (plausibly a decl-manager array index the engine fills once per chunk's build, not something resolved by name at load time — this specific mechanism is still INFERRED, not traced into any code, because there is no TNO runtime image to trace it into).

The 154-mismatch question (verifier's "missed" item), now explained: pass 1's own probe_tail.py reported "154 mismatches (pattern not found nearby)" for chunk1 without investigating it. Re-run with the search window widened from 64 to 256 bytes (probe_tail_v2.py, all 14 chunks): total mismatches drop from 30 506 to 2 793, and 100% of what remains is type sound — confirming the verifier's suspicion that 64 bytes was simply too narrow for some tails (consistent with sound's occasional 133/205-byte tails above), not a sign of a different, unexplained format.

Zähler A, substantially resolved (was fully open after pass 1): re-checked the "count of entries whose name is the first occurrence in this chunk" guess directly: counter A equals the number of distinct (type,name) pairs in that chunk, confirmed exact in 12 of 14 chunks (e.g. chunk1: 3508 distinct pairs = counter A exactly) and off by a tiny amount (16 of 15069 in chunk0, 1 of 21400 in chunk2 — both <0.1%, plausibly this project's own resync parser picking up a handful of extra/fewer entries, not a different counting rule) in the remaining 2. Not pinned to 100% exactness in all 14, but strong enough to treat as answered rather than open.

What is still open, explicitly not attempted this round:

  • Whether a hand-built new entry (synthetic trio + size fields + a tail matching this pattern, appended at the end of both the index's entry list and the .resources payload area) is even accepted by this project's own ResourceIndex.load() resync parser, let alone the real engine — not built or tested this round. Building and byte-level self-testing a writer was judged out of scope for the time available (unlike r2_newassets.md's TNC work, which had a runtime image to validate the engine's own lookup path against — TNO has none, so even a working writer here would only prove "our own reader accepts it," not "the game does"). Round 2's per-chunk-scope finding above makes this concretely easier than pass 1 left it: the writer only needs max(existing ids of this type in this one chunk) + 1, not a cross-chunk scan.
  • What a sound/particle/renderProg duplicate id actually indexes (shared cue/permutation table?) — newly measured as real this round, but not traced into any mechanism; irrelevant to the append-entry writer since §5's designs never touch these types.

Net answer: structurally more promising than TNC's IDCL (no checksum, no header hash chain, a resync-tolerant parser), and this round adds real, previously-unverified facts (the tail's exact shape, its per-chunk — not global — scope, and which types the "unique id" property actually holds for) — but turning that into a working append path, proven even at the "our own tooling round-trips it" level TNC's r2_newassets.md reached, is unbuilt and is the single most concrete next step (§6).

4. Inherit / load-order traps — does TNO have a dlc_0-style trap?

No equivalent exists, and the reason is structural, not luck (PROVEN, by code + file-listing read):

  • TNO's base/master.index names exactly 14 chunkN.index/ chunkN.resources pairs, flat, no DLC folders, no patch_*.resources (confirmed: ls of the TNO install root lists chunk0..chunk13 plus master.index and nothing else archive-shaped; contrast with TNC's base/+dlc/dlc_0..5/+patch_1..4.resources tiers that docs/patchvorrang.md/the TNC custom-map memory note are all about). There is no "which layer wins" question to get wrong, because there is only one layer.
  • wolfsdk.patch.Installation.lookup/find() is built once, over all self.archives (= all 14 chunks, always, every apply() call — not a subset chosen per-mod or per-map), and find() returns every hit for a (type, name) key across however many of the 14 chunks carry it (confirmed by code read, §2 above). _apply() then iterates for archive, entry in hits: and writes to every one. Measured duplication is real and non-trivial (§1.4: 444 raw ai/* entries for 255 distinct names, a 1.74× average) — so this "patch every copy" behaviour is load-bearing, not a theoretical safety net: a mod that only patched the first hit would silently leave 189 duplicate AI entries unpatched.
  • The one genuinely open question in this space, carried over unresolved from docs/ladepfade.md Teil 1 (not re-tested here, no game launch): whether fs_overlaypath/loose files in base/generated/ could let a loose file on disk shadow an archived decl of the same path. If that is real, it would be a different kind of trap than TNC's (a loose-file override silently beating an applied archive patch) — docs/ladepfade.md itself says this needs a 10-minute in-game experiment (its own "E1–E3") that was out of scope for a read-only round and still is.

One real trap this round did find, inside the decl layer itself, not the archive layer: the §1.4 "FAILED HYPOTHESIS" above — assuming a TNC-style shared-root AI lever exists in TNO when it structurally does not (252/255 decls override their own health). Anyone porting a TNC AI-difficulty recipe to TNO by analogy would silently affect almost nothing.

5. Three ready-to-build TNO mod designs

All three use only mechanisms independently confirmed safe in §2/§1 above (set on an existing path, or file with a verbatim donor array already shipping in retail) — no new patcher capability required, no new archive entries needed.

5.1 AI health slider, corpus-wide (generator script, set)

What: scale every ai/* entityDef's aiHealth.components.components[0].max and .cur by a constant factor (e.g. ×0.4 for "glass cannon enemies", ×2.5 for "bullet sponge"). Both max and cur must move together — moving only max leaves an enemy's actual spawn HP at the old (lower or higher) cur value, since the decl ships both set to the same number in every sampled entry (§1.4).

Why this needs a generator, not three hand-written edits: §1.4 proved 252 of 255 ai/* entityDefs carry their own override — there is no single shared decl to edit.

Build steps:

  1. Enumerate every (type="entityDef", name starts "ai/") key via Installation.lookup, de-duplicated by name (the loader already patches every chunk copy per §4 — the generator only needs one representative read per name to compute the new value).
  2. For each, Decl(text) (wolfsdk/decl.py), read edit.aiHealth.components.components[0].max/.cur, skip the 3 decls missing the path (§1.4's negative control — set would raise DeclError on them anyway, so skipping is both correct and the cheap path).
  3. Emit one mod.json with ~252 "set" assets: {"entityDef:ai/<name>": {"set": {"edit.aiHealth.components.components[0].max": "<new>", "edit.aiHealth.components.components[0].cur": "<new>"}}}.
  4. No new decl type, no file replacement, no risk beyond what widerstand's 11-asset set edit already proved end-to-end (§2).

5.2 Full arsenal from chapter 1 (file, exact retail donor text)

What: replace entityDef:player/game/wolf/c01/c01p1's startingInventory block (pristine: num = 1, one item, inventoryDecl = "weapon/knife46cutonly" — a chapter-specific knife variant, not plain weapon/knife as pass 1 imprecisely said; corrected this round by direct re-read, does not change the recipe since it's a full-block replace either way) with the exact array already shipping in player/game/wolf/c05_07_09_15/c15 (§1.5):

startingInventory = {
  num = 6;
  item[0] = { startSlot = "BACKPACK"; inventoryDecl = "weapon/knife"; }
  item[1] = { startSlot = "BACKPACK"; inventoryDecl = "weapon/laserkraftwerk"; }
  item[2] = { startSlot = "EQUIPPED"; inventoryDecl = "weapon/assaultrifle1960"; }
  item[3] = { startSlot = "BACKPACK"; inventoryDecl = "weapon/shotgun"; }
  item[4] = { startSlot = "BACKPACK"; inventoryDecl = "weapon/ar60marksman"; }
  item[5] = { startSlot = "BACKPACK"; inventoryDecl = "weapon/handgun"; }
}

Why this is "ready", not just an idea: moddingflaeche-tno.md §4.4 already proposed this exact move in the abstract ("den startingInventory-Block von c16p0 nach c01p1 kopieren", i.e. "copy the startingInventory block from c16p0 to c01p1"); this round turns it into an exact recipe by reading the real retail text of the richest donor (c15, not c16p0 — c16p0 does not exist as a key in the 30-name census, c15 does and is confirmed richer than the next-best c11p1/c14p1 candidates) and confirming every one of its 6 inventoryDecl targets is a real, resolvable weapon decl name (all appear as weapon: entries elsewhere in the same corpus, §1.1/§1.5). Build: copy _wolfsdk_backup's c01p1.decl text verbatim, string-replace only the startingInventory = { num = 1; … } span with the block above, ship as {"file": "assets/entityDef__player_game_wolf_c01_c01p1.decl"} — one asset, zero guessing, matches supersoldaten_c15's already-proven "copy and surgically edit one span" build pattern.

5.3 AI accuracy slider, 10 decls (set, single real combat lever)

What: set every tier of accuracySettings.accuracySettings[0..5].spread to a new value, in each of the 10 weapon/ai/* decls that carry their own copy of the field (§1.1: shotgun, assaultrifle1960, pistol, drone, robot, mg60/supersoldat1960, wol000d_pistol, assaultrifle1960rocket, mechadeathshead_laser, prototyperobot_lkw). Corrected this round (§1.1's inheritance-aware re-check, see "Pass-1 errors"): editing just these 10 decls' spread actually reaches 26 of the 29 weapon/ai/* decls corpus-wide, because 16 more decls inherit the field transitively from one of these 10 (e.g. submachinegun1946 inherits from assaultrifle1960) — only weapon/ai/baton, weapon/ai/fists/supersoldat1946 (genuinely melee) and weapon/ai/mg46/supersoldat1946 (unexplained standalone exception) are unreachable by this design. 10 edited decls, 26 affected — a bigger reach than pass 1 claimed, at the same build cost. Two directions in one mechanism: set all 6 tiers to 0 per decl (enemies hit every shot they take — "deadeye" mode) or scale every tier up by e.g. 2× (much more forgiving aim — an accessibility/easy-mode variant, distinct from the HP-based easy mode in §5.1 and composable with it).

Why this is the clean lever and not trackingParms: §1.4 found TNO's shared trackingparms/aim_default decl has no hit-probability field (parms.item[] is tracking speed, not spread) — the field TNC's doc describes under the same decl type name does not exist in TNO's copy. The actual, verified accuracy knob lives on the weapon side instead, and because weapon/ai/* decls are referenced by name from AI behaviour (not duplicated per enemy instance), 10 edited decls affect every AI that fires one of these 10 weapons corpus-wide — the one genuine single-point combat lever this round found for TNO AI (contrast with §5.1's health, which has none).

Build: 10 "set" assets, each with 6 leaf-path edits (edit.accuracySettings.accuracySettings[N].spread), all paths confirmed present in the pristine decls read this round — no file replacement needed anywhere in this design.

PROVEN

  • TNO's entityDef AI/player decl shape (aiHealth, actorEditable.damageGroups, faction, startingInventory, perkAbilities/perks/upgradeInfo) matches TNC's field paths exactly, confirmed by direct pristine read (§1.4/§1.5).
  • weapon/handgun_base's widerstand diff is exactly 3 hunks, matching the mod's own claimed fields, confirmed byte-for-byte against the pristine backup (§1.1).
  • damage/tungsten/mg46 pristine lacks cuttableRadius/ragdollOnDeath; both exist live, inserted by a "file" edit (§1.2).
  • projectile/handgun pristine lacks aimAssistRange entirely; it exists live (zielhilfe_alle), confirming the loader can insert brand-new decl keys via "file" (§0/§2).
  • 252/255 TNO ai/* entityDefs carry their own aiHealth override; only 3 do not (§1.4, full corpus count).
  • wolfsdk/decl.py:apply_patch() rejects any "set" path absent from the target decl's own parsed values (code read, §2).
  • wolfsdk/resources.py:Archive.rebuild() has no path to insert a new (type,name) entry; it raises on any payload key matching no existing entry (code read, §3).
  • TNO's archive tier is flat (14 chunks via master.index, no DLC/patch layer) and Installation.find()/apply() patches every duplicate copy across all 14 chunks in one call (code read, §4).
  • The 13-byte entry tail in chunkN.index is 9 zero bytes + a BE u32 id that is unique per chunk, per type (0 collisions in any of the 14 chunks) for every decl-ish type checked (entityDef, weapon, damage, ammo, projectile, aiBehavior, material, trackingParms, faction, gamemodesp, soundmixing) — but not unique across chunks (the same id legitimately recurs for unrelated content in different chunks) and not unique even within one chunk for sound/particle/renderProg/ cuttableResource (real same-chunk duplicate ids measured directly). Round-2 correction of a pass-1 overgeneralization; measured on all 14 chunks' real bytes (§3, re_probes/tnodecls/probe_tail_v2.py through _v5.py).
  • docs/formats.md's open "Zähler A" question is substantially resolved: it equals the count of distinct (type,name) pairs in that chunk, exact in 12 of 14 chunks, off by <0.1% in the other 2 (§3).
  • All 8 currently-installed TNO mods applied cleanly against the real 14-chunk archive set (journal.json: 377 assets, 0 missing, state: applied) — the patcher mechanism works end to end on live data, independent of the harness's generic in-game claim.
  • 16 of the 19 weapon/ai/* decls lacking their own accuracySettings[].spread inherit it transitively from one of the 10 base decls that carry it; only 2 are genuinely melee with no spread concept and 1 (mg46/supersoldat1946) is a real unexplained exception — round-2 fix of a pass-1 negative-control defect, resolved by tracing every inherit = chain (§1.1, re_probes/tnodecls/probe_weaponai_inherit.py).

INFERRED

  • That the per-(chunk,type) sequential tail id is assigned at content-build time, once per chunk (not resolved by name at load time, and not a single global counter across the install) — plausible from its behaviour (confirmed per-chunk unique, confirmed NOT per-name-consistent across chunks), not traced into any code (no TNO runtime image exists to trace into).
  • That a new entry appended as the last row of both the index's entry list and the .resources payload area would respect the ascending-offset invariant and thus not reproduce the known FileTable is out of order crash — reasoned from the invariant's stated cause, never built or tested.
  • That accuracySettings[].spread is the primary AI hit/miss lever (as opposed to some other, unread field) — supported by it being the only numeric field named "spread"/"accuracy" found on the 10 ranged weapon/ai/* decls (and, transitively, 16 more via inherit), but the actual to-hit math is in the encrypted .text and was not traced.
  • That sound/particle/renderProg/cuttableResource's within-chunk duplicate tail ids index some shared cue/permutation table (rather than being a parsing artifact) — plausible given the duplicate groups share the same decl name (e.g. 50 identically-named sound entries sharing one id), not traced into any code and not needed for any §5 design.

FAILED HYPOTHESES

  • "A TNC-style shared AI-health root decl exists in TNO, so one edit cascades corpus-wide." Refuted by direct count: 252/255 ai/* entityDefs override aiHealth themselves (§1.4).
  • "trackingParms:trackingparms/aim_default carries TNO's AI miss-chance field, the same shape TNC's decl of that name has." Refuted by direct read: TNO's copy has no aimError/trackerRange key, only tracking-speed fields (§1.4). The real lever is the weapon-side accuracySettings[].spread instead (§1.1, used in §5.3).
  • My own first-pass reads of weapon/handgun_base, damage/tungsten/mg46, and projectile/handgun from the live install, initially taken as "what retail ships" — caught and corrected by re-reading from _wolfsdk_backup once the backup folder was noticed (§0); flagged here so the same mistake isn't repeated by a reader skimming only this report.
  • "The 19 weapon/ai/* decls without their own accuracySettings[].spread have no spread concept at all" — refuted by tracing inherit = chains: 16 of the 19 inherit a working spread from one of the 10 base decls; a pass-1 negative control that skipped the exact inheritance check this report itself applies elsewhere (§1.1, verifier-found, round-2 fix).
  • "The 13-byte tail's 9-zero-plus-BE-u32-id pattern increments by exactly 1 and never repeats, full stop" — refuted as a blanket claim: true only per chunk and only for decl-ish types; sound/particle/renderProg/ cuttableResource have real same-chunk duplicate ids (§3, verifier-found, round-2 fix).

Open questions / next step

  1. Build and self-test an append-entry writer for chunkN.index+ .resources, at the level r2_newassets.md reached for TNC (own parser round-trips a synthetic new entry) — still the concrete, scoped next step, still not built. Round 2 removed one blocker: the per-type tail id only needs to be unique within the one chunk being edited (confirmed, not just assumed, §3), so the writer's id-assignment rule is now known (max(existing ids of this type in this chunk) + 1) and Zähler A is understood well enough to compute a correct replacement header value (count of distinct (type,name) pairs, §3). What's still unbuilt: the writer itself, and whether this project's own ResourceIndex.load() resync parser (let alone the real engine) accepts a hand-built entry — no TNO runtime image exists to check the latter.
  2. Whether fs_overlaypath/loose base/generated/ files can shadow an archived decl (§4) — docs/ladepfade.md's own unfinished E1 experiment, relevant here because it's the one place a TNO-specific load-order trap could still exist, just not in the archive-precedence shape TNC has.
  3. A TNO runtime image (the TNC-side tools/decomp.py equivalent) would turn several INFERRED items here into PROVEN — in particular, what a sound/particle/renderProg duplicate tail id actually indexes (§3), and whether accuracySettings[].spread really is the engine's hit/miss lever (§1.1/§5.3). None of this round's findings required it to be useful, but these specifically do.

In-game test (for the maintainer)

(Translated from German.)

Not a single one of the three designs from §5 was started — everything below is the smallest test step, not a result.

  1. The easy-to-check variant, design 5.2 (full arsenal in chapter 1): build the mod (one .decl file, entityDef:player/game/wolf/c01/c01p1, startingInventory as in §5.3 above), install it with wolfsdk apply, load chapter 1. Expected: the weapon wheel immediately shows six weapons (knife, Laserkraftwerk, assault rifle equipped, shotgun, sniper variant, pistol) instead of only the knife. This is the lowest-risk of the three tests — a single file replacement with exactly the values that chapter 15 itself uses in the original game.
  2. Design 5.3 (AI accuracy): set one of the ten weapon/ai/* decls (e.g. weapon/ai/assaultrifle1960) experimentally to spread = 0 in all six levels, play a single early encounter with rifle carriers (e.g. chapter 1). Expected: clearly more frequent hits by the enemies, without anything else changing (no new animation, no new sound). Shows whether accuracySettings[].spread really is the hit lever (§1.4 INFERRED) or whether the engine reads it differently than assumed here.
  3. Design 5.1 (AI health generator): before the full 252-decl run, first test one enemy type by hand — set ai/enemies/default's aiHealth.components.components[0].max/cur to e.g. 5 (as already proposed in docs/moddingflaeche-tno.md for TNC), play a short encounter with standard enemies. Expected according to §1.4: only the three non-combat decls would be affected if one touches only the root instead — that directly confirms whether the "no shared lever" finding measured in this report holds, before the 252-decl generator is built.

Files

File Content
re_probes/tnodecls/probe_tail.py Pass-1: read-only tail-byte measurement over chunk1.index only (§3), re-runnable; superseded in scope by _v2–_v5 below but kept for its raw-tail printout.
re_probes/tnodecls/probe_tail_v2.py Pass-2: same measurement widened to all 14 chunks, window=64 vs 256, per-type duplicate-id summary (§3, "Pass-1 errors").
re_probes/tnodecls/probe_tail_v3.py Pass-2: per-chunk vs cross-chunk id-uniqueness check for 7 mod-relevant types (§3).
re_probes/tnodecls/probe_tail_v4.py Pass-2: confirms cross-chunk id reuse maps to different names, not the same decl (§3).
re_probes/tnodecls/probe_tail_v5.py Pass-2: within-chunk duplicate-id check for sound/particle/renderProg/cm vs entityDef (§3).
re_probes/tnodecls/probe_cuttable.py Pass-2: within-chunk duplicate-id check for cuttableResource (§3).
re_probes/tnodecls/probe_weaponai_inherit.py Pass-2: weapon/ai/* corpus census + inherit = chain resolution for the accuracySettings[].spread negative control (§1.1, "Pass-1 errors").
re_probes/tnodecls/pristine_*.decl (7 files) Pristine (_wolfsdk_backup) extracts of every decl quoted in §1, saved so a reader can diff them against the live install without touching the game dir again.
re_probes/agent_reports/r6_tnodecls.md This report.

Related: Research Report: R2 Newassets, Research Report: R3 Newassetsv, Research Report: R4 Ai Decls, Research Report: R7 Tno Add Entry, TNO Modding Surface, File Formats, Modding Limits, Asset Loading Paths, Patch Precedence.