#87 root cause: the level-crossing zone-record send was missing -- observers never saw damage
The binary's effect watcher (FUN_0042aa2c) raises ForceUpdate(DamageZoneUpdateModelFlag) when a zone's damageLevel CROSSES a band-descriptor threshold (FUN_0042a5f4, master-gated on entity+0x28 & 0xc) -- that send is how every other pod's replicant learns zone levels mid-fight. The port's band hub kept only the graphic-state branch, so an observer's copy sat at 0.0 until destruction: no enemy hull darkening, no doll movement, ever (the night-10 "no armour discoloration" report). Restored the level branch with the already-reconstructed DescriptorCrossed (@0042a5f4) + the master gate on both branches (mesh swap still runs on every instance). 2-node verified: A-side armour watcher pushed 114 level changes; every replicant peak matched the master's finals to 4 decimals (dtorso 0.9321 == 0.9321). Render path cleared separately (gotcha #23 discharged): BT_ARMOR_FORCE 0/1 A/B captures prove the tint renders (hull -> charcoal at 1.0). Solo perception is the authentic economy: 2-25 pt lasers vs 68-185 pt pools. Bench: zonewalk.sh node A now carries BT_ARMOR_LOG + BT_SHOT_EVERY (the standing zone-replication receipt) + digest_walk.py; armorpx.sh is the force-pair pixel rig. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
cb5426cc7f
commit
fe48accb6b
@@ -1009,6 +1009,48 @@ returned NULL) -- the cascade "ran" and touched nothing, which is why blown-off
|
||||
arms left firing gun pods across every chassis (Conn Man's three-chassis audit).
|
||||
See reconstruction-gotchas #25.
|
||||
|
||||
## Zone-LEVEL replication -- the observer's picture (#87 root cause, 2026-08-03) [T1 decomp / T2 verified]
|
||||
|
||||
How an OBSERVER (replicant holder) learns a mech's zone damageLevels, end to end:
|
||||
|
||||
1. Damage messages do NOT echo locally: `Entity::Dispatch` on a REPLICANT
|
||||
forwards to the master's node and returns (ENTITY.cpp:244) -- there is no
|
||||
broadcast-bus application. The MASTER alone consumes `TakeDamage`.
|
||||
2. The master ships levels via zone UPDATE RECORDS
|
||||
(`DamageZone::Write/ReadUpdateRecord`, `DamageZoneUpdateModelBit`), gated by
|
||||
`ForceUpdate(DamageZoneUpdateModelFlag)`.
|
||||
3. **Who raises the flag (the binary's effect watcher `FUN_0042aa2c @0042aa2c`,
|
||||
the BT analog of the engine's `EntityEffectWatcher` -- which itself never
|
||||
runs for mechs: mech zones carry BAND DESCRIPTORS, the engine `ExplosionTable`
|
||||
stays NULL and `EntityEffectWatcher` is constructed nowhere in the image):**
|
||||
* level branch (zone changedFlags & 4): if the rise CROSSES a band-descriptor
|
||||
threshold (`FUN_0042a5f4` = `Mech__DamageZone::DescriptorCrossed`) AND the
|
||||
entity is a MASTER (`(entity+0x28 & 0xc) == 0`) -> `*(entity+0x18) |= 2`
|
||||
(= ForceUpdate(DamageZoneUpdateModelFlag)).
|
||||
* gstate branch (changedFlags & 8): ForceUpdate unconditionally (masters).
|
||||
So observers track a fight at **band-crossing granularity** -- not per hit.
|
||||
4. Consumption on the observer is passive: `ReadUpdateRecord` writes the level,
|
||||
the armour watcher (`TickArmourDamage`) re-reads zones every frame and pushes
|
||||
the draw-op tint; the doll gauges read `damageLevel*100` continuously.
|
||||
|
||||
**The port had dropped the level branch** -- mechdmg.cpp's band hub ForceUpdated
|
||||
only on `graphicState != Exists`, so every observer's copy sat at 0.0 until a
|
||||
zone DESTRUCTED: no enemy hull darkening, no observer-side doll movement, ever
|
||||
(the "no armour discoloration" night-10 report). Restored 2026-08-03
|
||||
(mechdmg.cpp band loop: `DescriptorCrossed(prev, level)` + master gate on both
|
||||
branches). Verified 2-node: A-side watcher pushed 114 level changes; every
|
||||
replicant peak matched the master's final level to 4 decimals (dtorso 0.9321
|
||||
both sides).
|
||||
|
||||
Render-path note (gotcha #23 discharged): the tint itself was pixel-proven with
|
||||
`BT_ARMOR_FORCE` A/B captures -- all-zones 1.0 renders the hull charcoal (0.1x
|
||||
floor), so "no discoloration" was never the renderer. Solo/master-side
|
||||
perception is the authentic ECONOMY: 2-25 pt laser events vs 68-185 pt zone
|
||||
pools = single-digit % levels = a 2-10% darkening + a doll tick of a few color
|
||||
indices. The cockpit doll maps all 28 zones (L4GAUGE.CFG:4788-4814 cmArmor
|
||||
lines -- feet + lower legs included); a "leg shots never show" report is those
|
||||
zones' levels peaking ~0.08-0.13, not a wiring gap.
|
||||
|
||||
## The hit-location CYLINDER -- raw-bytes audit + chassis->table map (2026-08-02) [T1]
|
||||
|
||||
Full independent verification of the type-29 DamageLookupTable streams, parsed
|
||||
|
||||
@@ -183,6 +183,7 @@ rows until sanity fails (`python + struct`, see the session commits `cc2b109`/`2
|
||||
- `class Damage { damageType(enum Collision/Ballistic/Explosive/Laser/Energy), damageAmount, damageForce, surfaceNormal, impactPoint, burstCount }`. [T1]
|
||||
- Weapon effect id: **"explode" = 13** (`Explosion::Make`). [T2]
|
||||
- `DestroyEntityMessage(id,size)` removes an entity — but a killed mech STAYS (a WRECK); death = a STATE transition (`SetGraphicState(DestroyedGraphicState)`), NOT removal. Issuing removal-on-death is the P5 teardown bug (do not). [T2]
|
||||
- **BT effect watcher (zone replication + band effects)** [T1, 2026-08-03]: ctor `FUN_0042a984 @0042a984` (hooks entity+0xbc, allocs oldLevel[damageZoneCount@+0x11c]); Execute `FUN_0042aa2c @0042aa2c` — per zone: flag&4 → `FUN_0042a664` DescriptorForLevel + `FUN_0042a5f4` DescriptorCrossed(old,new) → **crossed && master (`(entity+0x28 & 0xc)==0`) → `*(ushort*)(entity+0x18) |= 2`** (= `ForceUpdate(DamageZoneUpdateModelFlag)`, the zone-record send); flag&8 → `FUN_0042a6c4` DescriptorForGraphicState + the same master-gated ForceUpdate; descriptor → effect via `FUN_0043663c/FUN_004364e4` (renderer mgr @`DAT_004efc94+0x38`); changedFlags reset when (master && !pending-update) or replicant. Zone band table @ zone+0xd4; level @+0x158; gstate @+0x78. Engine `EntityEffectWatcher`/`ExplosionTable` are DEAD for mechs (table NULL, watcher never constructed — band descriptors replace them). Port: mechdmg.cpp band hub. See [[combat-damage]] §Zone-LEVEL replication.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -70,6 +70,13 @@ emulator** (⚠ `NotationFile::ReadText` expects NUL-SEPARATED lines). [T2]
|
||||
console LAUNCH). SIX bugs fixed to get here (dead-reckoner install, replicant-motion DeadReckon,
|
||||
master emission threshold, emission gated on RunningMission, the console-must-LAUNCH fact, replicant
|
||||
validity). [T2]
|
||||
- **Zone-level replication (#87, 2026-08-03):** an observer's copy of a mech learns zone damageLevels
|
||||
ONLY from zone update records, and the master sends those on BAND-THRESHOLD CROSSINGS (binary effect
|
||||
watcher `FUN_0042aa2c`: `DescriptorCrossed` + master gate → `ForceUpdate(DamageZoneUpdateModelFlag)`)
|
||||
plus every graphic-state change — never per hit. Damage messages do NOT echo locally (replicant
|
||||
`Dispatch` forwards to the master and returns). The port had only the gstate branch (observers saw
|
||||
0.0 until destruction); the level branch was restored + 2-node verified (replicant peaks == master
|
||||
finals to 4 decimals). Full mechanism: [[combat-damage]] §Zone-LEVEL replication. [T1/T2]
|
||||
- **Wire-format bug class found+fixed:** MakeMessages replicate RAW over TCP, so string payload must be
|
||||
INLINE (`char[N]` at the binary offsets), not a `const char*` pointer (garbage cross-pod). Check
|
||||
EVERY MakeMessage for pointer payloads. [T2]
|
||||
|
||||
@@ -106,6 +106,13 @@ SERVOS the torso twist + aim elevation until the centered reticle's pick ray
|
||||
and advances. `[walk] ZONE/FIRE/HOLD` lines on A pair with `[dmghit]` on B.
|
||||
Launch: `bash scratchpad/night10/zonewalk.sh` (relay included; NO kill timer —
|
||||
the session stays up for observation; teardown = taskkill btl4).
|
||||
Node A also carries `BT_ARMOR_LOG=1` + `BT_SHOT_EVERY=300 BT_SHOT_PREFIX=zwA`
|
||||
(added for #87): the armour watcher's `[armor] ... level P -> Q` lines on A are
|
||||
the OBSERVER-side receipt that B's zone levels replicated (the walk is the
|
||||
standing zone-replication bench — A-side peaks must match B's `[dmghit]`
|
||||
finals), and the numbered captures are the pixel record of the enemy hull
|
||||
darkening. Digest: `python scratchpad/night10/digest_walk.py` (per-zone hits,
|
||||
amt/hit, level first→last on B; replicant peaks seen by A; zone-repl lines).
|
||||
Companion scalpels: `BT_ASPECT_TEST=1` (the zero-premise frame probe — 4
|
||||
world-cardinal self-impacts + one at each weapon's physical MUZZLE, the
|
||||
left/right anchors that caught the #124 reflection-vs-rotation error);
|
||||
|
||||
Reference in New Issue
Block a user