Authentic target acquisition LIVE: reticle slew + pick-ray lock + aimed zone damage (task #36)

The engine Reticle model (MUNGA/RETICLE.h [T0]) reconstructed end to end:
- Mech::targetReticle is a real Reticle member bound to the TargetReticle
  attribute (0x1d), per the RP VTV analog (VTV.h targetReticle).
- Crosshair slew: mouse -> client rect -> reticle coords (the pod stick
  free-aim channel's dev-box stand-in); BT_AIM="x y" pins it headless.
  LMB fires lasers / RMB missiles (alongside SPACE/CTRL).
- Pick ray: the ACTIVE eye publishes pos + LookAtRH basis (BTSetAimCamera,
  L4VIDRND view-write site) + the render loop publishes proj._22;
  BTGetAimRay builds the world ray, Mech::PickRayHit slab-tests it against
  the collision template's ExtentBox via the engine's BoundingBox::HitBy
  (local frame; clips the Line at entry) -> world hull point.
- Designation: the mech under the crosshair designates (sticky; re-hover
  refreshes; cleared when the target leaves the roster at burial); the
  entity target slots 0x37c/0x388/0x38c feed the whole weapon path.
- Aimed fire: while HOT the impact point is the PICKED hull point -> the
  STEP-6 cylinder lookup resolves the zone under the crosshair (verified:
  center-aim -> head-band zone 13 dominant). Off-crosshair the sticky
  designation converges on center mass.
- HUD: the aim group draws at the slewed position ([0x9a] translate,
  contained by push/pop); the designator ring tracks the target's
  projected point (subB9 hot / subB8 designated, BTProjectToReticle);
  edge arrows when off-screen/behind.
- AUTHENTIC gating: no fire arc exists in the binary (FireWeapon fires
  whenever HasActiveTarget, part_013.c:7758) -> BT_FIRE_ARC is now an
  explicit OPT-IN presentation clamp; the hardwired gEnemyMech lock and
  the projectile path's gEnemyMech fallback are removed.
- Fixed en route: every renderable rebuild stomped mCamera back to the
  chase eye (start-inside silently lost the cockpit camera; the aim feed
  exposed it). BTL4VideoRenderer::mViewInside persists the chosen view.

Verified headless: BT_AIM="0 0" -> HOT lock, pick hits the hull face at
exact range, aimed zones resolve; BT_AIM="0.8 0.3" -> no lock, zero
damage, zero missile launches; kill chain completes to wreck + smoke.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
arcattack
2026-07-08 20:57:11 -05:00
co-authored by Claude Fable 5
parent 6988821525
commit d78bde066d
10 changed files with 541 additions and 185 deletions
+69 -1
View File
@@ -72,6 +72,7 @@
// AUTHENTIC GROUND MODEL ctor half (task #15): complete BoxedSolid type for the
// collisionTemplate/collisionVolume extent reads + the template bottom lift.
#include <BOXSOLID.hpp>
#include <line.hpp> // Line/Ray -- the PickRayHit slab test (task #36)
// STEP 6 cylinder hit-location table. dmgtable.hpp pulls in no subsystem
// headers (only Plug + mechrecon + <vector>), so it is safe here -- unlike the
// real subsystem headers, whose classes collide with mech.cpp's local stubs.
@@ -435,6 +436,67 @@ Point3D
return local;
}
//
// Ray pick against this mech (task #36 -- the engine Reticle's "pick point
// intersection testing"): transform the world ray into the mech's local frame
// (rotation part only for the direction), slab-test it against the collision
// template's ExtentBox via the engine's own BoundingBox::HitBy (which clips
// the Line's length to the entry distance), and return the world hit point.
// The same collision volume the movement/collision code uses; the hit point
// then drives the cylinder hit-location table at damage delivery (aimed
// shots resolve to the zone under the crosshair).
//
Logical
Mech::PickRayHit(const Point3D &start, const Vector3D &dir,
Scalar max_range, Point3D *hit_world)
{
BoxedSolid *tmpl = GetCollisionTemplate();
if (tmpl == 0)
return False;
// world -> local: point via the full inverse, direction via the inverse
// rotation (transform a second point and difference -- exact for the
// orthonormal localToWorld, no scale).
Point3D localStart;
localStart.MultiplyByInverse(start, localToWorld);
Point3D worldTip(start.x + dir.x, start.y + dir.y, start.z + dir.z);
Point3D localTip;
localTip.MultiplyByInverse(worldTip, localToWorld);
Vector3D localDir(localTip.x - localStart.x,
localTip.y - localStart.y,
localTip.z - localStart.z);
Scalar dlen = (Scalar)Sqrt(localDir.x*localDir.x + localDir.y*localDir.y
+ localDir.z*localDir.z);
if (dlen < 1e-6f)
return False;
localDir.x /= dlen; localDir.y /= dlen; localDir.z /= dlen;
BoundingBox box(*(const ExtentBox *)tmpl); // the template's extents
Line ray(localStart, UnitVector(localDir.x, localDir.y, localDir.z), max_range);
const Logical hitOK = box.HitBy(&ray); // clips ray.length to the entry
if (BTEnvOn("BT_AIM_LOG", 0))
{
static int s_n = 0;
if ((++s_n % 60) == 1)
DEBUG_STREAM << "[pick] box=(" << tmpl->minX << ".." << tmpl->maxX
<< ", " << tmpl->minY << ".." << tmpl->maxY
<< ", " << tmpl->minZ << ".." << tmpl->maxZ
<< ") lstart=(" << localStart.x << "," << localStart.y << "," << localStart.z
<< ") ldir=(" << localDir.x << "," << localDir.y << "," << localDir.z
<< ") hit=" << (int)hitOK << " len=" << ray.length << "\n" << std::flush;
}
if (!hitOK)
return False;
if (hit_world != 0)
{
Point3D localHit;
ray.Project(ray.length, &localHit); // start + length*dir (local)
hit_world->Multiply(localHit, localToWorld); // local -> world
}
return True;
}
//
// The roster torso (sinkSourceSubsystem @0x438, ClassID 0xBC5) -- parity with
// the binary's *(mech+0x438).
@@ -532,7 +594,7 @@ const Mech::IndexEntry
ATTRIBUTE_ENTRY(Mech, CurrentSpeed, legCycleSpeed), // 0x1a (existing @0x348)
ATTRIBUTE_ENTRY(Mech, MaxRunSpeed, reverseStrideLength), // 0x1b (existing @0x34c = run/top speed)
ATTRIBUTE_ENTRY(Mech, EyepointRotation, eyepointRotation), // 0x1c (real member -- the eye reads it per frame)
ATTRIBUTE_ENTRY(Mech, TargetReticle, attrPad), // 0x1d
ATTRIBUTE_ENTRY(Mech, TargetReticle, targetReticle), // 0x1d (real Reticle struct -- task #36)
ATTRIBUTE_ENTRY(Mech, FootStep, attrPad), // 0x1e
ATTRIBUTE_ENTRY(Mech, AnimationState, attrPad), // 0x1f
ATTRIBUTE_ENTRY(Mech, ReplicantAnimationState, attrPad), // 0x20
@@ -681,6 +743,12 @@ Mech::Mech(
weaponRoster.Construct(0); // FUN_004a4df6(this+0x1ef)
damageableSubsystems.Construct(0); // FUN_004a4e41(this+499)
// The target reticle (task #36): armed + pick-testing. The Reticle ctor
// zeroes position/target; the pilot's slew + the pick feed it per frame.
targetReticle.reticleState = Reticle::ReticleOn;
targetReticle.pickPointingOn = True;
targetReticle.reticleElementMask = Reticle::AllEnabledGroup;
for (int i = 0; i < 5; ++i) // FUN_0043ad4f x5
{
telemetryFilter[i].Initialize(15, 0.0f); // this[0x1f8..0x204]