Targeting correction: reticle = TORSO BORESIGHT, not a free mouse cursor (task #39)

Colleague flagged that targeting is torso-locked with no fine cursor.
Correct: the pod stick's yaw drove the TORSO TWIST
(MechControlsMapper -> HUD::SetFreeAimSlew(stick_x) @cockpit+0x28C,
gated on torso-horizontal-enabled; hud.hpp:167) and reticlePosition
@HUD+0x1FC is COMPUTED by HudSimulation from the mech pose quaternion +
target geometry, zeroed to centre with no target (part_013.c:5680) --
never a free-floating cursor. The engine Reticle struct is general
(shared with Red Planet); BT drives it from the torso boresight.

Fix:
- Removed the mouse-cursor slew (a mis-sourced stand-in from the
  RP-shared struct). The crosshair is now the torso boresight:
  BTTwistToReticleX(torsoTwist) = tan(twist) projected through the live
  per-axis projection. Dead-centre on the fixed-torso BLH
  (TorsoHorizontalEnabled=0); you aim by steering the whole mech.
- BT_AIM="x y" retained as the headless test harness.

Verified: face-to-face spawn -> HOT lock + aimed zone hits (36);
BT_FORCE_TURN circling -> 31 no-target vs 1 HOT (steering off-target
drops the lock, as it must). KB swept (combat-damage / gauges-hud /
open-questions); checkctx CLEAN.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
arcattack
2026-07-08 23:29:42 -05:00
co-authored by Claude Fable 5
parent 24ea7f13af
commit a8a56c57c9
5 changed files with 82 additions and 81 deletions
+38 -35
View File
@@ -32,41 +32,44 @@ 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, `engine/MUNGA/RETICLE.h` [T0]):** the
engine `Reticle` struct (the mech's **"TargetReticle" attribute** — the same one the reticle
renderable ctor binds) carries `reticlePosition` (screen 1..+1, SLEWABLE — the mechmppr
`freeAimSlew`), `pickPointingOn`, `rayIntersection` (Point3D), `targetEntity`, and
`targetDamageZone`: the pilot slews the crosshair; the engine **pick-rays through it** into the
3D world; the entity (and the specific DAMAGE ZONE) under the crosshair become the target. The
mech's three slots are this pick's result: `0x37c` = rayIntersection, `0x388` = targetEntity,
`0x38c` = targetDamageZone (so aimed shots hit the zone under the crosshair — the aimed-damage
path). `Reticle::ReticleElements` has a `TargetDesignatorOn` box + direction-arrow bit (the HUD's
lock box; the reticle's off-screen turn arrows point at it). Once locked, `Emitter::FireWeapon`
converges on the target with NO aim/arc test (part_013.c:7758: `range <= weaponRange → damage
*(mech+0x388)`) — convergence-on-lock IS authentic; the skill is keeping the crosshair on the
enemy. `MechWeapon::UpdateTargetState` (`FUN_004b9bdc` [T1]): `targetWithinRange = dist <
(1 hostZoneDamage) × weaponRange` — damage DEGRADES weapon reach; that flag is the HUD pip's
lit state. The 0x388 WRITER (the per-frame reticle→mech copy) is in an un-exported decomp gap.
**Port status: RECONSTRUCTED + runtime-verified (task #36, 2026-07-08) [T2].** The chain:
`Mech::targetReticle` (real `Reticle` member, bound to the TargetReticle attr 0x1d) ← slew from
the MOUSE (dev-box stand-in for the pod stick free-aim; `BT_AIM="x y"` pins it headless) →
`BTGetAimRay` (L4VIDEO; the ACTIVE eye publishes pos+LookAtRH basis via `BTSetAimCamera`, the
render loop publishes proj._22) → `Mech::PickRayHit` (world ray → local frame →
`BoundingBox::HitBy` slab test vs the collision template's ExtentBox → world hit point) → the
entity target slots. The slots MIRROR the pick EACH FRAME: locked while the crosshair is ON the
mech, no target otherwise — keeping the crosshair on the enemy IS the gunnery. (⚠ A first cut
made the designation STICKY — once hovered, locked forever — which made aim irrelevant after the
first acquisition; play-tested wrong, and the binary treats no-target as a FREQUENT live state:
the fire path re-checks 0x388!=0 at every step, part_013.c:7689/7727, and the HUD range feed has
a permanent no-target default, :5636. Corrected 2026-07-08.) While locked the impact point = the
PICKED hull pointSTEP-6 resolves the zone under the crosshair (verified: center-aim at the
head band → zone 13 dominant). The HUD
draws the aim group at the slewed position ([0x9a] translate list, contained by push/pop), the
designator ring at the target's projected point (subB9 hot / subB8 designated,
`BTProjectToReticle`), and the edge arrows when off-screen/behind. `BT_FIRE_ARC` is now an
EXPLICIT OPT-IN presentation clamp (unset = authentic no-arc). The old hardwired lock, the
±30°-default cone, and the projectile path's gEnemyMech fallback are all REMOVED. LMB fires
lasers / RMB missiles (with SPACE/CTRL). ⚠ A view-selection bug was fixed en route: every
**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) →
`Mech::PickRayHit` (world ray → local frame → `BoundingBox::HitBy` slab test vs the collision
template's ExtentBox → world hit point) → the entity target slots. The slots MIRROR the pick EACH
FRAME: locked while the boresight is ON the mech, no target otherwise — **steer to keep the enemy
under the centred crosshair; that IS the gunnery** (verified: face-to-face spawn → HOT lock;
`BT_FORCE_TURN` circling31 no-target vs 1 HOT). (⚠ Two earlier cuts were wrong and are
corrected: a STICKY designation — locked forever after first hover, made aim irrelevant; and a
MOUSE cursor slew — mis-sourced from the RP-shared struct, gave a free cursor the pod never had.
Both removed.) While locked the impact point = the PICKED hull point → STEP-6 resolves the zone
under the crosshair (verified: center-aim → head-band zone 13 dominant). The HUD draws the aim
group at the boresight ([0x9a] translate), the designator hotbox at the target's projected point
(subB9 hot / subB8 designated, `BTProjectHotBox`/`BTProjectToReticle`), and the edge arrows when
off-screen/behind. `BT_FIRE_ARC` is now an EXPLICIT OPT-IN presentation clamp (unset = authentic
no-arc). The old hardwired lock, the ±30°-default cone, and the projectile path's gEnemyMech
fallback are all REMOVED. LMB fires lasers / RMB missiles (with SPACE/CTRL). ⚠ A view-selection
bug was fixed en route: every
renderable rebuild stomped `mCamera` back to the chase eye (btl4vid `mViewInside` now persists
the chosen view — the aim camera feed and the V toggle both depend on it).
+2 -1
View File
@@ -126,7 +126,8 @@ 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` (SetMatrix, rebuilt on slew move).
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).
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 `blx_cop`
canopy shell renders black and is hidden by name filter (`BT_INSIDE_COCKPIT=1` shows it) — see
+10 -7
View File
@@ -51,13 +51,16 @@ authentic path scoped.
these are MODE flags — in the basic test mission the inert Myomers / dormant valve is likely AUTHENTIC
(advanced damage off); wiring makes them RESPOND when a mission enables the mode, not necessarily change
the basic-mission behavior. The MessageBoard feed is separate (StatusMessagePool, below).
- **✅ Authentic target acquisition RECONSTRUCTED (task #36, 2026-07-08)** — the `Reticle`
pick-ray chain is LIVE (see [[combat-damage]] Targeting for the full port map): mouse slew →
pick ray → per-frame lock (crosshair ON the mech = locked; off = none) → aimed zone damage +
designator ring; `BT_FIRE_ARC` is opt-in-only now. Residue: (a) the binary's own per-frame reticle→mech copy is still in an
un-exported gap (our writer follows RETICLE.h semantics [T3 dynamics]); (b) the pod's REAL slew
device channel (stick free-aim via the controls mapper / `freeAimSlew` @HUD+0x28C) is bridged
by the mouse — wire the real channel when pod hardware lands; (c) pre-burial, the pick still
- **✅ Authentic target acquisition RECONSTRUCTED (tasks #36/#39, 2026-07-08)** — the `Reticle`
pick-ray chain is LIVE (see [[combat-damage]] Targeting for the full port map): the crosshair =
**torso boresight** (NO free-aim mouse — the pod stick twisted the torso; you steer to aim) →
pick ray → per-frame lock (boresight ON the mech = locked; off = none) → aimed zone damage +
designator hotbox; `BT_FIRE_ARC` is opt-in-only now. Residue: (a) the binary's own per-frame
reticle→mech copy is still in an un-exported gap (our writer follows RETICLE.h + the
HudSimulation pose derivation [T3 dynamics]); (b) our cockpit view is body-mounted, so on a
TWIST-CAPABLE mech the crosshair should deflect with the torso twist (`BTTwistToReticleX` is
wired for it) AND the torso should visibly lead the legs — verify on a twist mech when one is
in play (the BLH is fixed-torso, boresight always centred); (c) pre-burial, the pick still
tests the sinking wreck's collision box at its parked position (pre-existing wreck-targeting
behavior).
- **Cockpit HUD leftovers (tasks #35-#38 residue, 2026-07-08)** — the reticle + 7 weapon pips +
+14
View File
@@ -165,6 +165,20 @@ void BTClientToReticle(float mx, float my, float cw, float ch,
*ry = y;
}
//
// Torso-twist angle (radians) -> reticle X (task #39): the crosshair marks
// where the torso guns point relative to the body-mounted view. A boresight
// ray at horizontal angle `twist` from forward hits camera-space
// xc = d*tan(twist) at depth d, so ndc_x = tan(twist)*P11 and
// rx = ndc_x * (vw/vh). Zero for a fixed torso (dead-centre boresight).
//
float BTTwistToReticleX(float twist_rad)
{
if (!gBTAimCamValid || gBTAimP11 <= 0.0f || gBTAimVpH <= 0.0f)
return 0.0f;
return (float)tan((double)twist_rad) * gBTAimP11 * (gBTAimVpW / gBTAimVpH);
}
//
// Crosshair (reticle coords) -> world pick ray. reticle -> NDC:
// ndc_x = rx * vh/vw (the reticle x unit is half the viewport HEIGHT),
+18 -38
View File
@@ -1241,12 +1241,18 @@ void
}
sPrevV = vNow;
// RETICLE SLEW (task #36): the pod slewed the target reticle
// with the stick free-aim channel; the dev-box stand-in is the
// MOUSE -- absolute cursor position mapped through the client
// rect into reticle coordinates (centered, +y down, unit =
// half the client height -- the dpl2d frame). BT_AIM="x y"
// pins the crosshair for headless harness runs.
// RETICLE = TORSO BORESIGHT (task #39 correction): the pod had
// NO free-aim cursor -- the binary's HudSimulation computes
// reticlePosition from the mech's POSE quaternions
// (part_013.c:5652+, a rate-limited quat->euler of the torso
// aim), i.e. the crosshair marks where the TORSO GUNS point
// relative to the view, and you aim by steering the mech /
// twisting the torso (the pod's right stick). The earlier
// mouse-cursor slew was a mis-sourced stand-in and is
// REMOVED. Our cockpit view is body-mounted, so the
// crosshair deflection = the torso twist projected to screen
// -- identically ZERO on the fixed-torso BLH (dead-centre
// boresight). BT_AIM="x y" remains as the headless harness.
{
static int sAimEnv = -1;
static float sAimEnvX = 0.0f, sAimEnvY = 0.0f;
@@ -1261,39 +1267,13 @@ void
gBTAimX = sAimEnvX;
gBTAimY = sAimEnvY;
}
else if (focused && pFg != 0)
else
{
typedef int (__stdcall *CurFn)(POINT *);
typedef int (__stdcall *StcFn)(void *, POINT *);
typedef int (__stdcall *CrFn)(void *, RECT *);
static CurFn pCur = 0; static StcFn pStc = 0; static CrFn pCr = 0;
if (pCur == 0)
{
HMODULE u = GetModuleHandleA("user32.dll");
pCur = (CurFn)GetProcAddress(u, "GetCursorPos");
pStc = (StcFn)GetProcAddress(u, "ScreenToClient");
pCr = (CrFn)GetProcAddress(u, "GetClientRect");
}
void *wnd = pFg(); // focused == our window
POINT cp; RECT rc;
if (pCur && pStc && pCr && wnd != 0 &&
pCur(&cp) && pStc(wnd, &cp) && pCr(wnd, &rc) &&
rc.right > rc.left && rc.bottom > rc.top)
{
// client px -> reticle coords through the RENDERER
// (it undoes the backbuffer->client present stretch;
// a raw client-height mapping is only exact for a
// backbuffer-shaped window and skews the pick
// sideways otherwise).
extern void BTClientToReticle(float mx, float my,
float cw, float ch, float *rx, float *ry);
float rx = 0.0f, ry = 0.0f;
BTClientToReticle((float)cp.x, (float)cp.y,
(float)(rc.right - rc.left),
(float)(rc.bottom - rc.top), &rx, &ry);
gBTAimX = rx;
gBTAimY = ry;
}
// boresight = torso twist vs the body-mounted view,
// projected through the live per-axis projection.
extern float BTTwistToReticleX(float twist_rad);
gBTAimX = BTTwistToReticleX(gBTHudTwist); // BLH: 0
gBTAimY = 0.0f;
}
}
}