From 832bec0966fe163c89999a7eb717bc1cb14815fc Mon Sep 17 00:00:00 2001 From: Joe DiPrima Date: Sat, 8 Aug 2026 06:11:59 -0500 Subject: [PATCH] hud.cpp: byte-ground the HudSimulation constants -- all five were stand-ins under guessed names Found chasing the Thor "no range finder" report: _DAT_004b7ec4 was documented as two incompatible things -- the 0.75 LOCK damage threshold (mech4.cpp:6325) and a "heat threshold for HUD page visibility" valued 0.0f (hud.cpp:59). The .rdata settles it (reference/decomp/section_dump.txt): 4b7ec0 8be55dc3 0000403f 0000803f 0000c842 4b7ed0 00000000 _DAT_004b7ec4 = 0.75f _DAT_004b7ec8 = 1.0f _DAT_004b7ecc = 100.0f _DAT_004b7ed0 = 0.0f _DAT_004b7f90 = 0.0f mech4.cpp was right on both thresholds. hud.cpp's whole tuning block was wrong -- every entry a 0.0f/500.0f stand-in, and three of five names named the wrong mechanism: * ec4/ec8 are the fire-control LOCK limits (own HUD host zone < 0.75 damage, targeted zone < 1.0), NOT heat/page-visibility. A shot-up cockpit drops to "target held, no lock"; a dead zone cannot be re-locked. * ed0 is the shared ZERO -- the right-hand side of the range-slide Abs() idiom (`dt * 500.0 <= 0.0` picks the sign) and of an `== 0.0f` test at @0x28C. The 500 m/s slide rate is an IMMEDIATE (0x43fa0000). The old "MaxTorsoSlew = 500.0f" read that backwards. * f90 (FlickerFloor 0.0f) was the only correct entry. Its decay RATE is the object's own @0x298, not a constant -- the step-6 banner said "up to MaxTorsoSlew (500/sec)" and is corrected too (hud.cpp:229 already had it right, so the file disagreed with itself). All four wrong constants were DEAD (zero code uses; MaxTorsoSlew appeared only in a comment), so this changes no behaviour -- it stops the next reader trusting them. Renamed to what they are: LockOwnZoneDamageLimit, LockTargetZoneDamageLimit, RangeBias, HudZero. Builds clean. GAP FOUND, filed not fixed: HudSimulation subtracts _DAT_004b7ecc (100.0f) from RangeToTarget@0x1EC every frame while the timed flag @0x22C is set (timer @0x21C accumulates to @0x1D8, then both clear). Our targeting step does the 500 m/s slide but never this bias, so the authentic timed -100 m range offset is missing. What sets @0x22C is unidentified. -> open-questions. KB swept: no context/ or docs/ file repeated the wrong constants (gauges-hud's 0-1200 ladder / 500 m/s / pegs-at-1200 claims are all correct); the error was confined to hud.cpp. gauges-hud.md gains the byte-grounded table + the gap. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_018SgmXGNMXavXiafKXf9MDC --- context/gauges-hud.md | 17 +++++++++++++ context/open-questions.md | 12 +++++++++ game/reconstructed/hud.cpp | 50 ++++++++++++++++++++++++++++++-------- 3 files changed, 69 insertions(+), 10 deletions(-) diff --git a/context/gauges-hud.md b/context/gauges-hud.md index 84be613..417d0a8 100644 --- a/context/gauges-hud.md +++ b/context/gauges-hud.md @@ -557,6 +557,23 @@ and every instrument is now live [T2]:** transcription color bug caught by a period reference screenshot, 2026-07-09; same for the bottom bowtie carets @4569-4570); pegs at 1200 with no target; the DISPLAYED range slides at **500 m/s** toward the true pick range (HudSimulation :5652 [T1]). + **The HudSimulation tuning constants, read off .rdata 2026-08-08 [T1]** (`section_dump.txt` + rows ` 4b7ec0 8be55dc3 0000403f 0000803f 0000c842` / ` 4b7ed0 00000000`) — + `_DAT_004b7ec4` = **0.75f**, `_DAT_004b7ec8` = **1.0f**, `_DAT_004b7ecc` = **100.0f**, + `_DAT_004b7ed0` = **0.0f**, `_DAT_004b7f90` = **0.0f**. ec4/ec8 are the fire-control **LOCK** + limits (own HUD host zone < 0.75 damage, targeted zone < 1.0 — so a shot-up cockpit drops to + "target held, no lock", and a dead zone can't be re-locked); ed0 is the shared **zero** in the + range-slide `Abs()` idiom (the 500 is an immediate `0x43fa0000`, NOT a global). `hud.cpp` had + carried all five as 0.0f/500.0f stand-ins under guessed names ("SegmentTempLimit … heat + threshold for HUD page visibility" was neither heat nor page visibility) — corrected in place, + with the live lock/slide implementation staying in mech4.cpp's targeting step, which had both + thresholds right all along. + **GAP — the 100 m RANGE BIAS is NOT reconstructed [T1 read, unimplemented]:** HudSimulation + subtracts `_DAT_004b7ecc` (100.0f) from `RangeToTarget@0x1EC` **every frame while the timed + flag @0x22C is set**, accumulating @0x21C by `time_slice` until it reaches @0x1D8, then clearing + both. The port's targeting step (mech4.cpp) does the slide but never the bias, so whatever + in-game state sets @0x22C currently produces no range offset. Trigger for @0x22C not yet + identified — see [[open-questions]]. **VERDICT (Gitea #4, 2026-07-20): the "range slides in/out crazily while walking" report is AUTHENTIC behavior, not a bug [T2 measured].** Per-frame `BT_RANGE_LOG` traces (mech4.cpp, with an independent Möller-Trumbore cross-check `BTGroundRayHitExact` in btvisgnd.cpp) on scripted diff --git a/context/open-questions.md b/context/open-questions.md index 5c17f81..f0afa29 100644 --- a/context/open-questions.md +++ b/context/open-questions.md @@ -954,6 +954,18 @@ register. ⚠ The audit also flags the damage-economy item as SELF-CONTRADICTOR deterministically leave ~0 at [ebp-0xc]; or replicant-player reroute delivery differs). Port deviates safely (award=0). Curiosity, not a blocker. +## HUD range-bias @0x22C — read but NOT reconstructed (2026-08-08) [T1 read, unimplemented] +`HudSimulation` subtracts `_DAT_004b7ecc` = **100.0f** from `RangeToTarget@0x1EC` on **every frame +that the flag @0x22C is non-zero**, while accumulating a timer @0x21C by `time_slice` until it +reaches the limit @0x1D8, at which point BOTH the timer and the flag are cleared. So the authentic +HUD has a timed **−100 m range offset** state that our targeting step (mech4.cpp — which does +reconstruct the 500 m/s slide correctly) never applies. +**Open:** what SETS @0x22C. Candidates not yet checked — a targeting-computer damage/jam effect, an +ECM/spoof, or a weapon-lock transient. Found while correcting hud.cpp's stand-in constant block +(the values are now byte-grounded: ec4 0.75 / ec8 1.0 / ecc 100.0 / ed0 0.0 / f90 0.0). Details: +[[gauges-hud]] §Right ladder. Worth resolving before trusting any field report about the range +readout being wrong — it is a real behavioural gap, not a cosmetic one. + ## Rendering follow-ups (non-blocking) - ~~Per-pilot mech PAINT (color/badge/patch)~~ — **DONE 2026-07-17, verified live** (crimson MadCat + yellow VGL emblems + hip hazard stripes). Mechanics + the vehicletable color/badge/patch name diff --git a/game/reconstructed/hud.cpp b/game/reconstructed/hud.cpp index 8d1299a..f5b55df 100644 --- a/game/reconstructed/hud.cpp +++ b/game/reconstructed/hud.cpp @@ -52,15 +52,41 @@ struct HUDLayoutCheck }; // -// Tuning constants observed as read-only float globals adjacent to the -// HudSimulation body (.rdata, recovered from section_dump.txt). +// Tuning constants read as read-only float globals adjacent to the +// HudSimulation body. The VALUES BELOW ARE THE BINARY'S, read straight off +// the .rdata rows in reference/decomp/section_dump.txt [T1]: +// 4b7ec0 8be55dc3 0000403f 0000803f 0000c842 +// 4b7ed0 00000000 +// -> ec4 = 0.75f ec8 = 1.0f ecc = 100.0f ed0 = 0.0f +// +// CORRECTED 2026-08-08. Every entry here used to be a 0.0f/500.0f STAND-IN +// under a guessed name, and the names described the wrong mechanism: there is +// no "heat threshold for HUD page visibility" at 0x4b7ec4 -- ec4/ec8 are the +// fire-control LOCK damage limits, and "MaxTorsoSlew = 500.0f" at ed0 read the +// range-slide Abs() idiom backwards (ed0 is the ZERO; 500.0 is an immediate). +// The LIVE implementation of the lock rule and the range slide is mech4.cpp's +// targeting step, which had both thresholds right all along. These stay so +// the addresses resolve to the truth for the next reader. // static const Point3D HudZeroVector(0.0f, 0.0f, 0.0f); // DAT_004e0f74/78/7c -static const Scalar SegmentTempLimit = 0.0f; // _DAT_004b7ec4 (heat threshold for HUD page visibility) -static const Scalar TargetTempLimit = 0.0f; // _DAT_004b7ec8 -static const Scalar RangeBias = 0.0f; // _DAT_004b7ecc -static const Scalar MaxTorsoSlew = 500.0f; // _DAT_004b7ed0 -static const Scalar FlickerFloor = 0.0f; // _DAT_004b7f90 +// LOCK gate (a): your OWN HUD's host zone must be BELOW 75% damage, else +// fire-control lock is lost -- the `_DAT_004b7ec4 <= ownZone->damageLevel` +// arm of HudSimulation. A shot-up cockpit drops to "target held, no lock". +static const Scalar LockOwnZoneDamageLimit = 0.75f; // _DAT_004b7ec4 +// LOCK gate (b): the TARGETED zone must be below 1.0 damage (a whole-mech +// target checks zone 0), so a dead zone cannot be re-locked. +static const Scalar LockTargetZoneDamageLimit = 1.0f; // _DAT_004b7ec8 +// Subtracted from RangeToTarget (@0x1EC) every frame while the timed flag +// @0x22C is set (timer @0x21C accumulates to @0x1D8, then both clear). +// NOT reconstructed in the port's targeting step -- tracked, not implemented. +static const Scalar RangeBias = 100.0f; // _DAT_004b7ecc +// The shared ZERO: the right-hand side of the Abs() idiom on the range slide +// (`dt * 500.0 <= 0.0` picks the sign) and of an `== 0.0f` test at @0x28C. +// The 500 m/s slide RATE is an immediate literal (0x43fa0000), never this. +static const Scalar HudZero = 0.0f; // _DAT_004b7ed0 +// The decay FLOOR for horizontalTorsoOffset (@0x294) in FUN_004b7ed4. The +// decay RATE is the object's own @0x298, not a constant. (Value verified.) +static const Scalar FlickerFloor = 0.0f; // _DAT_004b7f90 // // Cross-family helper (definition lives in the mech game layer; declared here @@ -322,9 +348,13 @@ Logical // (+0x100), sliding at 500 m/s (:5652-5670), default 1200.0 with no // target; the compass Scalar @0x214 = yaw euler[0] + torso twist (:5676). // 6. Torso-horizon slew: horizontalTorsoOffset (@0x294) is moved toward the -// commanded torso heading at up to MaxTorsoSlew (500/sec), clamped to -// +/- horizontalLimit (@0x29C), then written to the graphic at -// mech +0x36C. The flicker helper (@004b7ed4) damps the settle. +// commanded torso heading, clamped to +/- horizontalLimit (@0x29C), then +// written to the graphic at mech +0x36C. The flicker helper (@004b7ed4) +// damps the settle: it decays @0x294 toward ZERO at the object's own +// @0x298 (horizontalMovementPerSecond) x time_slice. (CORRECTED +// 2026-08-08: this used to read "at up to MaxTorsoSlew (500/sec)" -- the +// rate is that per-object field, and there is no 500 constant here. The +// 500 m/s belongs to the RANGE slide in step 5, as an immediate.) // void HUD::HudSimulation(Scalar time_slice)