From f7cf9850b1d4fc13add7718a0aa50171e5054acd Mon Sep 17 00:00:00 2001 From: Joe DiPrima Date: Wed, 29 Jul 2026 09:06:28 -0500 Subject: [PATCH] crit panel: the roll has no caller and the sink is a stub -- filed as #80 Night-6 observation (Conn Man, with screenshot): armor panel showing damage, Critical damage display showing no crits. Investigated; it is a real port gap in three layers, stacked on one authentic fact. The authentic fact: the paper doll shows per-ZONE armor tint and the Critical view is a per-SUBSYSTEM list. Different data by design -- the panel staying dark while armor accumulates is correct right up until a subsystem takes critical damage. The gap: subsystem critical damage essentially cannot happen. 1. Mech__DamageZone::CriticalHit @0049ccc4 -- the authored roll, half the hit to armour, half to one critical subsystem by criticalWeight -- has ZERO callers in the port. A raw byte-scan of the binary finds exactly one call site, @0x4a0461, and it sits in the un-exported decomp gap (nothing covers 0x4a03xx-0x4a05xx, the same dark region as the targeting-pick writer). The trigger conditions are unknown. 2. MechSubsystem::TakeDamage is an empty bring-up stub (btstubs.cpp:179), so even a wired caller would measure a delta of zero through ApplyDamageAndMeasure. 3. The only live crit sources are zone destruction (SendSubsystemDamage) and ammo cook-off (DistributeCriticalHit), which pin subsystem damage directly -- so the panel can light after a zone is destroyed outright, never from accumulating fire. #28 (the vital-subsystem-crit death path) very likely shares this root and is cross-linked. Recovery plan on #80: raw-disasm the 0x4a04xx container for the trigger, dump MechSubsystem vtable 0050e210 slot +0x24 for the real TakeDamage body, wire both. Co-Authored-By: Claude Fable 5 --- context/combat-damage.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/context/combat-damage.md b/context/combat-damage.md index c89be24..4cc9172 100644 --- a/context/combat-damage.md +++ b/context/combat-damage.md @@ -394,6 +394,25 @@ plausibly intersected the mech's *cylinder* — the damage table is literally cy Deciding whether that distortion is material needs a per-hit theta probe + a wheel dump (slices × percent tables, MadCat) — see #73 on the tracker. +## ⚠ CRITS NEVER ROLL FROM WEAPON FIRE — the trigger is un-exported + the sink is a stub (2026-07-29) [T1] +The authored crit machinery exists and is reconstructed — `Mech__DamageZone::CriticalHit @0049ccc4` +(half the damage to armour, half to ONE critical subsystem chosen by `criticalWeight`, capped by +`damagePercentage`) — but **nothing in the port calls it**. Raw byte-scan of the binary: exactly ONE +call site, `@0x4a0461`, inside the **un-exported gap** (no decomp covers `0x4a03xx-0x4a05xx` — the +same dark region as the `0x37c/0x388/0x38c` pick writer), so the crit TRIGGER CONDITIONS (per-hit +chance? threshold crossing?) are unknown and unreconstructed. Second layer: +`MechSubsystem::TakeDamage` is an **empty bring-up stub** (`btstubs.cpp:179`), so even a wired +caller would measure a delta of 0 through `ApplyDamageAndMeasure` (and the weapon-family overrides +chain to the engine base whose private-zone write is the zero-`damageScale` no-op — see the +subsystem-zone finding above). Net: **the Critical view lights ONLY on zone destruction +(`SendSubsystemDamage`, direct pin) or ammo cook-off (`DistributeCriticalHit`, direct pin), never +from accumulating fire.** The DISPLAY is fine — the paper doll shows ZONE ARMOR, the Critical panel +shows SUBSYSTEM crits (different data by design); the panel's near-permanent emptiness is the gap. +Field signature (night 6, Conn Man): "Armor panel damage. Critical damage display did not show any +crits." **#28 (vital-subsystem-crit death path) is very likely blocked by the same missing caller.** +Recovery: raw-disasm the `@0x4a04xx` container (#60-class gap work) for the trigger; dump +MechSubsystem vtable `0050e210` slot `+0x24` for the real TakeDamage body; wire both. Tracker: #80. + ## Damage delivery + the real damage model `Entity::TakeDamageMessage(id, size, inflictingEntityID, zone, Damage&)` → `target->Dispatch`. **Base handler IGNORES zone==−1** (`Entity::TakeDamageMessageHandler`, ENTITY.cpp:878 — returns on