Torso: the TWIST goes LIVE -- electrical watchdog chain, centered crosshair, coherent controls (task #57/#58)
The MadCat torso twists, the view turns with it, and targeting follows. Three reconstruction fronts closed: THE ELECTRICAL WATCHDOG CHAIN (why the torso never powered up): - PowerWatcher::UpdateWatch reconstructed (@004b181c, the REAL registered Performance -- PTR @0050f5fc; Ghidra missed the fn start): the watchdog MIRRORS the watched subsystem's electrical level (+0x278), brownout downgrade when gen output <= minVoltage% x rated. @004b1804 relabeled ResetToInitialState (slot 10) -- the old "Simulation" tag was wrong. - The factory watcher-CONNECT pass reconstructed (vtable slot +0x38, @004aee2c/@004b1a40 byte-identical, recovered from raw exe bytes): watchedLink.Add(roster[watchedSubsystem]) on the master node. Was the SubProxy::Start() no-op -- every watchdog sat at 0 forever. - MinVoltageScale = 0.01 (a 10-byte x87 literal @0x4b1924; was 1.0f = permanent brownout) and PowerWatcher's Derivation chains its REAL base HeatWatcher (the HeatableSubsystem stand-in broke IsDerivedFrom for the whole Torso/Searchlight/ThermalSight family). - KB correction swept: derivation tag 0x50e604 = HEATWATCHER (not "HeatSink"); the btl4gaug heat-widget gate now tests it via the BTIsHeatWatcher bridge. THE CROSSHAIR (task #58 forensics, 6-agent workflow + live probes): - The VIEW is TORSO-MOUNTED: jointtorso -> jointeye -> siteeyepoint in every twist-capable .SKL; the camera + canopy ride the same hinge subtree through HingeRenderable's live matrix-stack compose -- ALREADY WORKING in the port. The crosshair stays screen-centered (center IS the boresight); the twist reads on the tape carets/compass/radar. - The real bug was the port's gBTAimX = tan(twist) slew (the falsified "body-mounted view" model): the camera already carried the twist, so the crosshair counter-slid to hull-forward and the fire ray with it. Deleted; the pick ray inherits the twist from the yawing eye basis. - Two instrumentation traps documented (chase-eye-as-default-camera, BT_FORCE_TORSO clobbering real joints -> the hook now only fills unresolved ones); an over-correcting explicit eye compose was added on those false readings and retired the same day. CONTROLS + REPLICATION: - Q/E spring-center on release (the axis is a twist-RATE demand; the old hold-deflection model drifted forever); X also zeroes the axis and pulses the authentic torso Recenter (@004b6918). M cycles control mode via the real CycleControlMode body. - Torso update-record DIRECTION fixed: engine truth is Write=serialize / Read=apply; @004b6a78 is the READ (was mislabeled Write) and the missing WRITE @004b6a1c recovered from raw disasm (recordLength 0x1C, twist/vel/rate at +0x10/14/18) -- kills the replicant's 0xCDCDCDCD -140-degree ghost twist. - Marching-ghost desync: 4 Standing-case guards zero stale reverse cycleSpeed (negative cadence passed the <= ZeroSpeed stop gate). - Kill credit rerouted to the OBSERVED killer (lastInflictingID -> killer's player link) -- kills count, target K/D populates. KB: subsystems.md (watcher chain), multiplayer.md (record direction), combat-damage.md + gauges-hud.md + cockpit-view.md (torso-mounted view re-correction), decomp-reference.md (new addresses + tag fix), open-questions.md (dead capability-roster loops 2-4, snapshot CD read). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
065c114590
commit
02cdfd6576
+14
-1
@@ -61,7 +61,20 @@ inverse(eyeWorld)`. No LookAt anywhere.
|
||||
- offset matrix = the segment's **GetBaseOffset()** (segment+0x74, the LOCAL rest transform) —
|
||||
NOT GetSegmentToEntity, NOT an upright basis;
|
||||
- parent = the eyepoint segment's **parent draw component** (`dcs_array[GetParentIndex()]`) —
|
||||
liveness (torso pose, gait, gyro) reaches the eye ONLY through this parent chain;
|
||||
liveness reaches the eye through this parent chain in the PORT TOO (task #58 FINAL): the
|
||||
twist path is `Torso::PushTwist → Joint::SetRotation → hinge rotationAmount →
|
||||
HingeRenderable::Execute` (multiplies the live hinge into the matrix stack), and the eye
|
||||
executes inside that scope (`jointtorso → jointeye → siteeyepoint`; VERIFIED: with the real
|
||||
jointtorso swept, the active cockpit `[eyefwd]` swings ~60° and the torso hinge renderable
|
||||
sits in the eye's parent chain). The CAMERA yaws with the twist, the canopy rides the same
|
||||
subtree, the crosshair stays screen-centered — see [[combat-damage]] Targeting.
|
||||
⚠ TWO instrumentation traps produced false "eye is frozen" readings first: (1) the headless
|
||||
default camera is the CHASE eye (root-parented, same DPLEyeRenderable class — gate probes on
|
||||
the COCKPIT eye being live); (2) `BT_FORCE_TORSO` used to override the joint names with the
|
||||
BLH demo pair (`jointshakey2`/`jointtshadow`), silently redirecting a MadCat sweep into the
|
||||
SHADOW hinge — the hook now only fills joints the resource left unresolved. An explicit
|
||||
eye-side twist compose added on those false readings DOUBLE-ROTATED the view (2× vs the 1×
|
||||
canopy) and is retired;
|
||||
- the `EyepointRotation` entity attribute (EulerAngles; Mech member, init 0).
|
||||
- **Combine order:** `FUN_0040b104(dst,A,B)` computes `dst = B × A` (translation from B) →
|
||||
the eye local = `baseOffset × R`, R applied FIRST in eye-local space. Recomputed only when the
|
||||
|
||||
+39
-23
@@ -32,29 +32,45 @@ player input). [T1]
|
||||
`mech+0x38c` = targeted sub-zone (−1=whole). Weapons cache hasTarget/targetPoint/muzzlePoint,
|
||||
refreshed each frame. [T1]
|
||||
|
||||
**The AUTHENTIC acquisition model (recovered 2026-07-08; corrected 2026-07-08, task #39):** THERE
|
||||
IS NO FREE-AIM MOUSE CURSOR. The pod had a stick whose yaw drove the **torso twist**
|
||||
(`MechControlsMapper` writes `HUD::SetFreeAimSlew(stick_x)` → cockpit+0x28C, gated on the torso's
|
||||
horizontal-twist being enabled — `mechmppr.cpp:735/762`, hud.hpp:167 [T1]); the crosshair marks
|
||||
where the **torso guns boresight** relative to the body-mounted view. You aim by **steering the
|
||||
mech / twisting the torso**, not by moving a cursor. The engine `Reticle` struct
|
||||
(`engine/MUNGA/RETICLE.h`) carries `reticlePosition`, `pickPointingOn`, `rayIntersection`,
|
||||
`targetEntity`, `targetDamageZone` — a general struct shared with Red Planet — but in BT
|
||||
`reticlePosition`/HotBoxVector (@mech-HUD+0x1FC) is **COMPUTED by HudSimulation** from the mech
|
||||
pose quaternion (`FUN_0040954c` quat→euler of mech+0x10C) + the target geometry, and **zeroed to
|
||||
centre when there is no target** (part_013.c:5680, `FUN_00408440(...,&DAT_004e0f74)` [T1]) — it is
|
||||
NOT a free-floating cursor. The pick then hits whatever the guns point at; that entity + its
|
||||
DAMAGE ZONE become the target (`0x37c` rayIntersection, `0x388` targetEntity, `0x38c`
|
||||
targetDamageZone → aimed-zone damage). Fixed-torso mechs (the BLH: `TorsoHorizontalEnabled=0`)
|
||||
boresight dead-ahead — the crosshair sits centred and you point the whole mech. Once locked,
|
||||
`Emitter::FireWeapon` converges with NO aim/arc test (part_013.c:7758). `MechWeapon::
|
||||
UpdateTargetState` (`FUN_004b9bdc` [T1]): `targetWithinRange = dist < (1 − hostZoneDamage) ×
|
||||
weaponRange`. The 0x388 WRITER (the per-frame reticle→mech copy) is in an un-exported decomp gap.
|
||||
**Port status: RECONSTRUCTED + runtime-verified (tasks #36/#39, 2026-07-08) [T2].** The chain:
|
||||
the crosshair = **torso boresight** (`BTTwistToReticleX(torsoTwist)`, L4VIDEO — tan(twist)
|
||||
projected through the live per-axis projection; **0 = dead-centre on the fixed-torso BLH**;
|
||||
`BT_AIM="x y"` pins it for headless tests) → `BTGetAimRay` (the ACTIVE eye publishes pos+LookAtRH
|
||||
basis via `BTSetAimCamera`, the render loop publishes proj._11/_22 + backbuffer dims) →
|
||||
**The AUTHENTIC acquisition model (recovered 2026-07-08; task #39; RE-CORRECTED 2026-07-13, task
|
||||
#58 crosshair-twist forensics):** THERE IS NO FREE-AIM MOUSE CURSOR — and **the VIEW is
|
||||
TORSO-MOUNTED, not body-mounted**. The cockpit eye hangs off the torso yaw hinge in every
|
||||
twist-capable skeleton (`jointtorso` (hinge-Y) → `jointeye` → `siteeyepoint`; all 14 twist-enabled
|
||||
Torso records name `TorsoHorizontalJoint=jointtorso`; `Torso::UpdateJoints` writes currentTwist
|
||||
into that joint each frame; VIEW = inverse of the LIVE joint chain, `FUN_004c22c4`
|
||||
part_013.c:11742, no de-twist anywhere [T1]). So when the torso twists, **the CAMERA yaws with
|
||||
the guns and the crosshair stays SCREEN-CENTERED** — truthfully, because screen center always IS
|
||||
the boresight; the world rotates past it. The twist reads on INSTRUMENTS: the bottom 21-tick tape
|
||||
carets, the compass (= body yaw + twist, part_013.c:5674-5676), the radar wedge
|
||||
(part_013.c:11836-11859) [T1]. The old "crosshair = torso boresight offset in a body-mounted
|
||||
view" model was falsified — it was never discriminated before because every prior live test used
|
||||
the fixed-torso BLH, where both models center the crosshair.
|
||||
The engine `Reticle` struct (`engine/MUNGA/RETICLE.h`) carries `reticlePosition` (a variable
|
||||
screen position −1..+1 [T0]) — the reticle CAN move, but its writer is in an un-exported decomp
|
||||
gap (`s_TargetReticle` has exactly ONE xref binary-wide: the read-side lookup part_014.c:5132);
|
||||
the only coherent twist-era mover is the **fixed-torso free-aim channel**: `MechControlsMapper`
|
||||
writes `HUD::SetFreeAimSlew(stick_x)` → cockpit+0x28C **only when torso twist is disabled**
|
||||
(part_013.c:339-345/381-387 [T1]); HudSimulation integrates it (negated, part_013.c:5717) into
|
||||
+0x294 clamped ±(+0x29C) → `mech+0x36c` every frame (:5735) — no exported consumer; the
|
||||
mech+0x36c → reticlePosition link is [T4]. NB: an earlier version of this section conflated
|
||||
`reticlePosition` with **HotBoxVector** (HUD+0x1FC — the TARGET designator HudSimulation zeroes
|
||||
with no target, part_013.c:5680); they are different things.
|
||||
The pick hits whatever the guns point at; that entity + its DAMAGE ZONE become the target
|
||||
(`0x37c` rayIntersection, `0x388` targetEntity, `0x38c` targetDamageZone → aimed-zone damage).
|
||||
Fixed-torso mechs (the BLH: `TorsoHorizontalEnabled=0` — no jointtorso in the skeleton at all)
|
||||
boresight dead-ahead. Once locked, `Emitter::FireWeapon` converges with NO aim/arc test
|
||||
(part_013.c:7758). `MechWeapon::UpdateTargetState` (`FUN_004b9bdc` [T1]): `targetWithinRange =
|
||||
dist < (1 − hostZoneDamage) × weaponRange`. The 0x388 WRITER is in the same un-exported gap.
|
||||
**Port status: RECONSTRUCTED + runtime-verified (tasks #36/#39/#58) [T2].** The chain: the
|
||||
cockpit eye inherits the twist AUTHENTICALLY through the draw traversal (`Torso::PushTwist` →
|
||||
hinge `rotationAmount` → `HingeRenderable::Execute` multiplies the live hinge into the matrix
|
||||
stack; the eye executes under `jointtorso → jointeye`; verified `[eyefwd]` sweeps with the real
|
||||
jointtorso — NO explicit eye-side compose exists, one briefly added on false "frozen eye"
|
||||
probe readings double-rotated the view and is retired, see [[cockpit-view]]); the crosshair sits
|
||||
at screen center (`gBTAimX = 0` — the old `BTTwistToReticleX` slew was THE bug: the camera
|
||||
already yawed, so the crosshair counter-slid to HULL-forward, "twisting leaves the crosshairs
|
||||
behind"; `BT_AIM="x y"` still pins it for headless tests) → `BTGetAimRay` (the ACTIVE eye
|
||||
publishes pos+basis via `BTSetAimCamera`, leveled in pitch, the twist yaw riding in the basis) →
|
||||
`Mech::PickRayHit` (world ray → local frame → `BoundingBox::HitBy` slab test vs the collision
|
||||
template's ExtentBox → world hit point).
|
||||
|
||||
|
||||
@@ -83,12 +83,24 @@ Identify a class at runtime with `sub->GetClassID()` (== `sub+4`). [T1]
|
||||
| **0x511830** | **MechWeapon**::ClassDerivations | The reticle AddWeapon loop (part_014.c:5386) hard-aborts on missing WeaponRange/PipPosition/… attrs for every member; part_012.c:9344 counts ready weapons; the Mech ctor roster at this[0x1ef] (`weaponRoster`, ex-mislabel "poweredSubsystems") ORs weapon `capabilityFlags`@+0x334. [T1] |
|
||||
| 0x50f4bc | PoweredSubsystem | btl4gau2 gauge wave. [T1] |
|
||||
| 0x50e3ec | HeatableSubsystem | btl4gaug widget gate. [T1] |
|
||||
| 0x50e604 | HeatSink | btl4gaug widget gate. [T1] |
|
||||
| 0x50e604 | **HeatWatcher** (task #57 CORRECTION — was "HeatSink") | `HeatWatcher::TestInstance` @4aec38 tests its OWN class vs this tag; the factory watcher-CONNECT loop (slot +0x38) selects on it; the btl4gaug widget gate ORs it after HeatableSubsystem (a HeatSink test there would be redundant — HeatSink : HeatableSubsystem). 0x50e590 = the HeatSinkBank (0xBBE) tag. [T1] |
|
||||
| 0x51155c | roster at Mech this[0x1eb] (`heatableSubsystems` label) | tag ≠ 0x50e3ec — the label is UNVERIFIED [T4]; arbitrate before relying on it. |
|
||||
| 0x50e4fc | roster at Mech this[499] (`damageableSubsystems` label) | [T3] |
|
||||
| 0x50bdb4 | Mech | emitter.cpp destroyed-check. [T1] |
|
||||
| 0x5121a8 | ProjectileWeapon-family (checked after 0x511830; reads ammo state @+0x364) | [T3] |
|
||||
|
||||
### Watcher electrical chain (task #57 — all [T1], several from RAW-BYTE recovery)
|
||||
| Address | What |
|
||||
|---|---|
|
||||
| @004aec54 | `HeatWatcher::CreateStreamedSubsystem` — parses `WatchedSubsystem=<name>` → segment index **+2** at resource+0xE4 |
|
||||
| @004aee2c / @004b1a40 | vtable slot 14 (+0x38) CONNECT bodies (HeatWatcher / PowerWatcher+Torso, byte-identical): master-gated `watchedLink(+0x114).Add(owner->roster[watchedSubsystem(+0x128)])`. **Ghidra missed both starts** — recovered from raw exe bytes (vtables.tsv rows have GAPS at skipped slots; dump vtable+slot*4 bytes when a slot looks absent) |
|
||||
| @004b181c | `PowerWatcher::UpdateWatch` — THE registered Performance (PTR @0050f5fc); watchdogAlarm(+0x184) MIRRORS watched subsystem's electrical level (+0x278); brownout→1 when `gen outputVoltage(+0x1DC) <= minVoltage(+0x180) × ratedVoltage(+0x1D8)`. Torso sims call it first-line |
|
||||
| @004b1804 | `PowerWatcher::ResetToInitialState` (slot 10) — old recon MISLABELED it "Simulation" |
|
||||
| 0x4b1924 | `MinVoltageScale` — a **10-byte x87 literal** (`0a d7 a3 70 3d 0a d7 a3 f8 3f` = **0.01**); the port had 1.0f → permanent brownout |
|
||||
| @004b6a78 | `Torso::ReadUpdateRecord` (slot 6; base 41bd34) — REPLICANT apply of twist extras +0x10/14/18. Old recon mislabeled it Write |
|
||||
| @004b6a1c | `Torso::WriteUpdateRecord` (slot 7; base 41c500) — MASTER serialize; recordLength=0x1C; **Ghidra missed the start** |
|
||||
| 41bd34 / 41c500 | base Subsystem Read (apply) / Write (produce) update-record fns — engine [T0]: **Write=produce, Read=apply**; don't flip |
|
||||
|
||||
---
|
||||
|
||||
## 3. Mech struct offsets (`Mech*`, binary layout)
|
||||
|
||||
+10
-3
@@ -146,7 +146,8 @@ queue. `SeekVoltageGraph`'s 4 Seek* attrs are a cluster-child (not config-called
|
||||
view only, via the recovered **dpl2d** 2D display-list API (recorders @0x487f34-0x488630; opcode
|
||||
map + coordinate model in `phases/phase-02-dpl2d-reticle.md`; port: `game/reconstructed/dpl2d.cpp`).
|
||||
Geometry is the ctor's hardcoded calibration (originX 0.35, originY 0.25, scaleY 0.5, right range
|
||||
ladder 0..1200 m, bottom heading tape, center cross + dot; tick ladders via FUN_004cd938). The
|
||||
ladder 0..1200 m, bottom TORSO-TWIST tape (NOT a heading tape — stale wording swept task #58; see
|
||||
the tape entry below), center cross + dot; tick ladders via FUN_004cd938). The
|
||||
range caret binds to the live target range (`BTSetHudTargetRange`, fed by mech4's targeting step).
|
||||
**Weapon pips:** the build loop (part_014.c:5386) registers EVERY subsystem
|
||||
`IsDerivedFrom(0x511830 = MechWeapon)` — lasers, PPCs AND missile launchers (BLH = 7 pips) — via
|
||||
@@ -196,8 +197,14 @@ and every instrument is now live [T2]:**
|
||||
around the hotbox point, +1/−11.5 vertical (K=2.8145 baked projection; the port uses the live
|
||||
per-axis projection) — switching to the left/right edge ARROW past ±1.6 or behind.
|
||||
- Reticle state Off/On + `PrimaryHudOn` (mask 0x20) picks full HUD vs the "simple X" list;
|
||||
the aim group translates by `reticlePosition` = the TORSO BORESIGHT (SetMatrix; NOT a free
|
||||
cursor — see [[combat-damage]] Targeting: the stick twists the torso, no mouse-aim exists).
|
||||
the aim group SetMatrix-translates by `Reticle::reticlePosition` (screen −1..+1 [T0]) every
|
||||
frame — but **NOT by torso twist** (task #58 CORRECTION): the VIEW is torso-mounted (the eye
|
||||
hangs off jointtorso → jointeye → siteeyepoint), so the crosshair stays SCREEN-CENTERED through
|
||||
a twist — screen center IS the boresight; the twist reads on the bottom tape/compass/radar
|
||||
wedge instead. The reticlePosition writer is un-exported (one xref binary-wide: the read-side
|
||||
lookup part_014.c:5132); its coherent use is the FIXED-torso free-aim channel (mech+0x36c
|
||||
[T4]). The old "translates to the torso boresight" wording here was the falsified
|
||||
body-mounted-view model — see [[combat-damage]] Targeting for the full re-correction.
|
||||
This recovery also CONFIRMS the HUD attr-table ids 4/5/6/8/0xA/0xB/0xC/0xD name↔use pairings
|
||||
(hud.hpp had flagged them uncertain). Deferred: PNAME1-8.bgf 3D marker chain. (The canopy shell
|
||||
is now authentic and shows by default — see [[cockpit-view]]; `BT_HIDE_COCKPIT=1` hides it.)
|
||||
|
||||
@@ -22,6 +22,21 @@ engine `AnimationInstance::Animate(dt, move_joints)` integrates `movement += Δt
|
||||
moves the body that far → feet plant by construction (no stride constant; k=1). Idle anims have
|
||||
z=0. The `.MOD` physics governed thrust VEHICLES, NOT walk gait. [T1]
|
||||
|
||||
## The MARCHING-GHOST desync — ROOT-CAUSED + FIXED (2026-07-13, live diagnosis) [T2]
|
||||
The long-parked "replicant walks in place while its master stands still" reproduced live with
|
||||
telemetry running: the master's `[gaitSM]` showed `state=0 cycleSpeed=-2.50725 kfCur=19` — BOTH
|
||||
channels stuck at a REVERSE cadence inside Standing. Mechanism: the walk-family stop gate
|
||||
(`cycleSpeed <= ZeroSpeed`, mech2.cpp) passes trivially for NEGATIVE (reverse) cadences, and
|
||||
several stand-entry paths (turn exit, terminal poses) never touch the cycle — so a reverse-stop
|
||||
enters Standing at full reverse cycle speed. The master looks still (case-0 never advances the
|
||||
clip; `kfCur` freezes) but the stale cycle REPLICATES, and the peer's replicant animates it =
|
||||
the marching ghost. A clean FORWARD stop decays the cycle first (`legSum ~3e-8` on the healthy
|
||||
node) — which is why the bug was intermittent and direction-dependent. FIX: all four
|
||||
Advance*Animation Standing cases zero their channel's cycle on entry + ForceUpdate(8) so the
|
||||
peer hears the stop immediately. (The signed-demand change (task #15) is what let negative
|
||||
cycles reach machinery written for forward decel.) The earlier "bob reduction + stutter"
|
||||
symptom family likely shares this root — re-observe after this fix before chasing separately.
|
||||
|
||||
## The two-channel gait (real controls)
|
||||
The BT `SequenceController` (`seqctl.cpp`, from `SelectSequence@004277a8`+`Advance@0042790c`) drives
|
||||
locomotion. Under `BT_REAL_CONTROLS` two channels run — **roles corrected 2026-07-09 (task #49) from
|
||||
|
||||
@@ -237,6 +237,30 @@ engine/port member. Wire sizes verified live (0x14/0x20/0x2c/0x78). Key mechan
|
||||
A TU-local stub type forced the `BTMapperSpeedDemandRaw(void*)` bridge (mech.cpp carries a local
|
||||
`struct MechControlsMapper` recon stub — typed cross-TU signatures mangle differently).
|
||||
|
||||
## Subsystem update-record DIRECTION + the Torso pair (task #57, 2026-07-13) [T0/T1]
|
||||
|
||||
**Engine semantics [T0 SIMULATE.cpp:270-297]: `WriteUpdateRecord(record, model)` PRODUCES the
|
||||
outgoing record from the object (master serialize; base sets `recordLength=sizeof`, stream
|
||||
advances by `recordLength` — variable-length records are the mechanism); `ReadUpdateRecord(record)`
|
||||
APPLIES the record into the object (replicant).** The names read backwards — do not flip them.
|
||||
Base fns in every subsystem vtable: slot 6 = 41bd34 (Read/apply), slot 7 = 41c500 (Write/produce,
|
||||
3 args).
|
||||
|
||||
- **Torso pair:** slot 6 `@004b6a78` = `ReadUpdateRecord` (apply: lastUpdateTime clock stamp +
|
||||
twistAtUpdate/twistVelocity/twistRate ← record +0x10/14/18). Slot 7 `@004b6a1c` =
|
||||
`WriteUpdateRecord` — **Ghidra missed the function start**; raw-disasm recovery: chains base,
|
||||
sets `recordLength=0x1C`, writes currentTwist/twistVelocity/twistRate → +0x10/14/18, snapshots
|
||||
`twistAtUpdate = currentTwist`.
|
||||
- **The bug this fixed:** the old recon had the APPLY body on the WRITE virtual (and no Write at
|
||||
all) — the master consumed its own uninitialized stream buffer (0xCDCDCDCD → twistAtUpdate =
|
||||
−4.316e8) and never serialized twist; the replicant MadCat pinned at −140° (the "torso ghost
|
||||
twist"). With the pair correct the replicant tracks `targetTwist = twistAtUpdate +
|
||||
twistRate·elapsed` cleanly (cur=0 when master still). [T2 live]
|
||||
- **Residual (authentic):** the initial full-state snapshot record does NOT carry the torso
|
||||
extras; the replicant's blind +0x10 read can pick up 0xCD fill ONCE at spawn. The binary does
|
||||
the same read; the twist clamp contains it (on the fixed-torso BLH it pins to ±0.01° —
|
||||
invisible). First real twist record overwrites it.
|
||||
|
||||
## Remaining (P6 phase 4 / Phase 7)
|
||||
The pod-LAN config (real IPs, bare-IP pilot entries); update-record velocity sourced from the body-channel
|
||||
projection (above). See [[open-questions]]. [T3]
|
||||
|
||||
@@ -21,7 +21,43 @@ authentic path scoped.
|
||||
- Pod specifics for Phase 8: the 7-monitor driver setup, the RIO cockpit I/O protocol, current
|
||||
Win10+wrapper pod config. Status: OPEN.
|
||||
|
||||
## BINARY-COVERAGE AUDIT discoveries (2026-07-13, 6-agent decomp census) [T1 verified-uncited]
|
||||
New unaccounted functionality no prior list knew (addresses verified absent from game/+context/+docs):
|
||||
- **`BTL4VideoRenderer::StartEntityEffectImplementation` @004d097c** (+helpers @4d0c14/@4d0c5c,
|
||||
~93 ln) — the per-zone EFFECT DISPATCHER, vtable-dispatched from the engine's
|
||||
StartEntityEffectMessageHandler (hence invisible to address-grep). Body fully mapped: zone
|
||||
position (+0x74), video index (+0xc4→+0xc0), jointToDCS socket (+0x3a4, in CLASSMAP),
|
||||
SearchList type 10, atoi-dispatch into the SAME effect-number bands the port already serves
|
||||
(>=1000 psfx ✓, 3-15 specialfx ✓, 104 SwapToWreck ✓). **The whole per-zone damage/destruction
|
||||
visual chain is dead without it. RECOMMENDED NEXT TARGET (S/M).**
|
||||
- **Six L4VIDRND effect-renderable bodies** @4540ac/45447c/455eb8/456cf0/4589e0/458e5c (~576 ln)
|
||||
incl. the 40-param ScalingExplosionRenderable ctor ExplosionScripts calls 6× — the live 1995
|
||||
bodies behind the STUBBED markers (wreck flames/booms land into these).
|
||||
- **FUN_00472480** (273 ln) — unnamed analog→discrete stepper Execute in the gauge band
|
||||
(hysteresis + key-repeat), no caller found; the one hole in "gauge system complete".
|
||||
Settle via vtable lookup (plausibly a config/roster scroller).
|
||||
- **FUN_00454a70** (193 ln) — the PNAME1-8/PLACE1-8.bgf MP name-billboard loader (the known
|
||||
"MP target identification" gap's exact function).
|
||||
- **Explosion::SplashDamage @0042fad0** — substantial, unreferenced, directly in the combat
|
||||
path; one-time diff vs engine EXPLODE.cpp would rule out 1995-vs-WinTesla falloff drift.
|
||||
- **The binary's own VelociRender BGF/BMF/BSL/VTX loader (~4,550 ln, dead code)** — a free
|
||||
ground-truth cross-check for [[bgf-format]]/[[asset-formats]] claims.
|
||||
Full ranked audit: the workflow output (2026-07-13); top-10 ranking cross-checked against this
|
||||
register. ⚠ The audit also flags the damage-economy item as SELF-CONTRADICTORY in the KB
|
||||
(task #8 "landed" vs the kShotDamage=12 bring-up residue) — audit before reworking.
|
||||
|
||||
## Deferred subsystems / feeds (authentic path scoped, marked in code)
|
||||
- **Factory capability-roster loops 2-4 are STILL DEAD (task #57 discovery).** mech.cpp's
|
||||
post-roster loops add to `heatableSubsystems`(0x51155c)/`weaponRoster`(0x511830)/
|
||||
`damageableSubsystems`(0x50e4fc) through the local `SubProxy` stub whose `IsDerivedFrom`
|
||||
RETURNS 0 — so those three mech-level rosters stay EMPTY (whatever consumes them sees
|
||||
nothing; weapons demonstrably work through other paths, so audit consumers before assuming
|
||||
impact). Loop 1 (the watcher CONNECT, 0x50e604) was reconstructed for real in task #57 —
|
||||
use the same bridge pattern (family-side derivation test + typed body) for the other three. [T2]
|
||||
- **Initial-snapshot torso record carries no twist extras**: the replicant's blind +0x10 read
|
||||
can latch 0xCDCDCDCD ONCE at spawn until the first real twist record; clamps contain it
|
||||
(invisible on fixed-torso mechs). Binary-authentic blind read — fix only if a visibly wrong
|
||||
spawn pose ever surfaces. See [[multiplayer]]. [T2]
|
||||
- **✅ 0xBD3 SubsystemMessageManager — UNTANGLED + LIVE (task #7, 2026-07-11) [T1/T2].**
|
||||
Both halves landed: (1) the factory case builds the REAL messmgr (ctor @0049bca4, 0x130,
|
||||
static reconstruction messmgr.cpp) cached at `mech+0x434` — the binary-wide census found
|
||||
|
||||
@@ -55,6 +55,35 @@ Making a base byte-exact GROWS every subclass — they must be re-based TOGETHER
|
||||
an untangle. NOT the valve/advanced-damage gate — that's the owning **BTPlayer** @mech+0x190, see
|
||||
[[combat-damage]]/[[open-questions]]). [T2]
|
||||
|
||||
## The watcher electrical chain (task #57, 2026-07-13 — the torso power gate)
|
||||
The Watcher branch is POWERED indirectly: a watcher WATCHES another roster subsystem and mirrors
|
||||
its electrical state. The full chain, byte-verified [T1]:
|
||||
- **Data:** the model entry `WatchedSubsystem=<name>` → segment index **+2** at resource+0xE4
|
||||
(`HeatWatcher::CreateStreamedSubsystem` @004aec54); the ctor @004aeb40 stores it at
|
||||
watcher+0x128 (`watchedSubsystem`).
|
||||
- **Bind (factory post-roster loop 1):** vtable slot 14 (+0x38) — `@004aee2c` (HeatWatcher) /
|
||||
`@004b1a40` (PowerWatcher/Torso override, byte-identical); **Ghidra missed both function
|
||||
starts** (recovered from raw bytes; the vtables.tsv rows have GAPS where the exporter skipped
|
||||
slots — dump the exe bytes at vtable+slot*4 when a slot looks missing). Master-gated
|
||||
(`(owner->simulationFlags & 0xC)==0 && (flags & 0x100)`); binds `watchedLink(+0x114).Add(
|
||||
owner->roster[+0x128][watchedSubsystem])`. Port: `BTWatcherWatchedIndex`/`BTWatcherBindTarget`
|
||||
bridges (heatfamily_reslice.cpp) called from the mech.cpp factory loop.
|
||||
- **Tick:** `@004b181c` = the REAL `PowerWatcher` Performance (PTR @0050f5fc → 004b181c) =
|
||||
`UpdateWatch()`: heat mirror (FUN_004aeac4) + `watchdogAlarm.SetLevel(watched->electrical
|
||||
level @+0x278)` + brownout downgrade to 1 when `gen->outputVoltage(+0x1DC) <= minVoltage(+0x180)
|
||||
× gen->ratedVoltage(+0x1D8)`. The Torso sims (@004b5cf0/@004b65f8) call it first-line.
|
||||
(`@004b1804` is slot-10 **ResetToInitialState**, NOT the Simulation — old recon mislabel, fixed.)
|
||||
- **MinVoltageScale = 0.01** — a 10-byte x87 literal at 0x4b1924 (`0a d7 a3 70 3d 0a d7 a3 f8 3f`);
|
||||
the port had 1.0f, making minVoltage 100× too big → the brownout latched every watchdog at 1.
|
||||
- **PowerWatcher::GetClassDerivations chains HeatWatcher** (real base) — the old HeatableSubsystem
|
||||
stand-in broke `IsDerivedFrom(HeatWatcher)` for Torso/Searchlight/ThermalSight and silently
|
||||
skipped them in the connect pass.
|
||||
- **Effect:** `Torso::ElectricalStateLevel()==Ready(4)` un-gates `effectiveTwistRate` — the MadCat
|
||||
torso twists at its authored 50°/s (±140° limits, roster 17 watching 15 → generator @10000V);
|
||||
the BLH is authentically fixed (`horizontalEnabled=0`, limits ±0.01°). [T2 live-verified]
|
||||
- **STILL DEAD:** factory loops 2-4 (heatable/weapon/damageable capability rosters) go through the
|
||||
`SubProxy` stub whose `IsDerivedFrom` returns 0 — they add NOTHING. See [[open-questions]].
|
||||
|
||||
## The four systemic checks (every subsystem)
|
||||
See [[reconstruction-gotchas]]: (1) shadowing (re-declared engine-base fields), (2) the `Wword` trap,
|
||||
(3) message-handler chaining, (4) entity validity. Plus resource-struct layout (must mirror the class
|
||||
|
||||
Reference in New Issue
Block a user