Boresight parallax FIXED + the #4/#5 verdict instrumentation (Gitea #16, #4, #5)

PARALLAX (#16): the pick/fire ray was anchored at mech.y+5.0 (a port
improvisation) while the sight line ran from the eyepoint (y=6.23) --
two parallel rays whose constant offset grew into the reported low-miss
as range closed (measured ry +0.072 @50u -> +1.54 @2.7u).  The decomp's
sight and pick share the eye origin (HudSimulation @4b7830 chain).  Fix:
the viewpoint mech's cockpit eye owns the aim-camera publish in BOTH
views, origin = its own eye translation; leveling + deliberate elevation
untouched; chase view now converges to cockpit ballistics (V cannot
change where shots land).  After: pick pinned to the crosshair (ry <=
5e-6) from 50u to point-blank; 26 laser + 8 missile center-mass hits at
3/4-screen.  Awaiting the reporters' approach-test.

VERDICT INSTRUMENTATION (#4 closed authentic, #5 verdict posted):
BT_RANGE_LOG per-frame pick tracing + BTGroundRayHitExact analytic
cross-check (0/8000 arena fall-throughs; cavern 6/8400 single-frame
grazes -- the 'crazy sliding' is the authentic world-pick + 500 m/s
slide over depth discontinuities); BT_AUD_TAIL StopNote/fade timing +
BT_FIRE_PULSE single-shot driver (the energy 'buzz' is the AUTHORED
charging loop: crescendo through recharge, 1.309s authored release,
measured within one frame).  CAVERN.EGG: solo cavern test egg.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
arcattack
2026-07-20 14:51:54 -05:00
co-authored by Claude Fable 5
parent d7158f1f82
commit 70eea6c1a4
13 changed files with 744 additions and 18 deletions
+151 -2
View File
@@ -156,6 +156,7 @@
#include <mechweap.hpp> // MechWeapon::GetExplosionResourceID (per-round detonation)
#include <mislanch.hpp> // MissileLauncher::ClassDerivations (splash-radius gate)
#include "btinput.hpp" // the CONTROLS.MAP + XInput binding engine
#include <string.h> // [aud-tail] strchr -- BT_FIRE_PULSE "on,off" parse (Gitea #5, instrumentation only)
#if !defined(PLAYER_HPP)
# include <player.hpp> // Player::VehicleDeadMessage -- the death->respawn notification (task #52)
#endif
@@ -2932,6 +2933,34 @@ void
sGotoDrive = (gv && *gv) ? 1 : 0;
}
gBTDrive.fireForced = sAutoFire;
// [aud-tail] (Gitea #5 verdict harness, additive): BT_FIRE_PULSE="on,off"
// (frames) pulses the forced trigger instead of holding it, so a SINGLE
// energy shot is followed by trigger silence -- the reported scenario
// (beam ends, buzz tail runs out alone). Overrides BT_AUTOFIRE's hold.
{
static int s_fpOn = -1, s_fpOff = 0, s_fpCount = 0;
if (s_fpOn < 0)
{
const char *fp = getenv("BT_FIRE_PULSE");
if (fp && *fp)
{
s_fpOn = atoi(fp);
if (s_fpOn <= 0) s_fpOn = 6;
const char *comma = strchr(fp, ',');
s_fpOff = comma ? atoi(comma + 1) : 600;
if (s_fpOff <= 0) s_fpOff = 600;
}
else
{
s_fpOn = 0;
}
}
if (s_fpOn > 0)
{
gBTDrive.fireForced =
((s_fpCount++ % (s_fpOn + s_fpOff)) < s_fpOn) ? 1 : 0;
}
}
if (sAutoDrive > 0.0f)
{
gBTDrive.forced = 1;
@@ -3324,7 +3353,18 @@ void
}
// STOP radius: "enemy" holds at weapon range so it can shoot
// instead of ramming; a fixed point drives right up to the spot.
const float stopDist = (s_goto == 2) ? 300.0f : 5.0f;
// BT_GOTO_STOP overrides (issue #16 parallax test: approach the
// dummy from range all the way in to 3/4-screen).
float stopDist = (s_goto == 2) ? 300.0f : 5.0f;
{
static float s_stopOv = -1.0f;
if (s_stopOv < 0.0f)
{
const char *so = getenv("BT_GOTO_STOP");
s_stopOv = (so && *so) ? (float)atof(so) : 0.0f;
}
if (s_stopOv > 0.0f) stopDist = s_stopOv;
}
const float stopDist2 = stopDist * stopDist;
const int arrived = (dist2 < stopDist2);
// publish for the mapper bridge (the real-controls path reads
@@ -3353,7 +3393,20 @@ void
// only from BT_AUTODRIVE). Drive forward toward the target and cut
// the throttle inside the stop radius so it holds at firing range.
gBTDrive.forced = 1;
gBTDrive.forcedThrottle = arrived ? 0.0f : 0.8f;
// BT_GOTO_THR: approach-speed override (issue #16 parallax test --
// a walking approach spans the render-live window so the curve can
// be sampled from far to near).
float gthr = 0.8f;
{
static float s_thrOv = -1.0f;
if (s_thrOv < 0.0f)
{
const char *to = getenv("BT_GOTO_THR");
s_thrOv = (to && *to) ? (float)atof(to) : 0.0f;
}
if (s_thrOv > 0.0f) gthr = s_thrOv;
}
gBTDrive.forcedThrottle = arrived ? 0.0f : gthr;
turn = gBTGotoTurn; // non-real-controls fallback path
}
}
@@ -4581,6 +4634,16 @@ void
Point3D hotPoint; // picked world point on its hull
Entity *pickTarget = 0; // what the boresight ray hit (mech/terrain)
Point3D pickPoint; // where it hit
// BT_RANGE_LOG (Gitea #4 VERDICT instrumentation -- uncommitted diag):
// capture the boresight ray + which pick tier won this frame, so the
// HUD-feed block below can trace the range chain per frame.
// rlPickClass: 0=no ray/no pick 1=mech 2=structure (static solid
// tree) 3=ground march (visual-terrain step march) 4=max-range
// default designation (nothing pickable within 1200)
int rlPickClass = 0;
int rlHaveRay = 0;
float rlRayS[3] = {0,0,0}, rlRayD[3] = {0,0,0};
float rlStructD = -1.0f; // sqrt range of the structure tier hit
static int gAimNoRay = 0, gAimGround = 0, gAimHits = 0; // 1Hz diagnostics
{
extern int BTGetAimRay(float rx, float ry, float outStart[3], float outDir[3]);
@@ -4731,6 +4794,9 @@ void
{
Point3D rayStart(rs[0], rs[1], rs[2]);
Vector3D rayDir(rd[0], rd[1], rd[2]);
rlHaveRay = 1; // BT_RANGE_LOG capture (issue #4)
rlRayS[0] = rs[0]; rlRayS[1] = rs[1]; rlRayS[2] = rs[2];
rlRayD[0] = rd[0]; rlRayD[1] = rd[1]; rlRayD[2] = rd[2];
// MP TARGETING (task #46): walk EVERY other living mech (the solo
// dummy AND every peer replicant, from the live-mech registry) and
// pick the CLOSEST one the boresight ray strikes -- generalising
@@ -4776,6 +4842,7 @@ void
structDist = dx*dx + dy*dy + dz*dz;
structPoint = sp;
haveStruct = true;
rlStructD = (float)Sqrt(structDist); // BT_RANGE_LOG (issue #4)
static float s_wp = 0.0f; s_wp += dt;
if (getenv("BT_GROUND_LOG") && s_wp >= 1.0f)
{
@@ -4795,6 +4862,7 @@ void
{
pickTarget = hotTarget; // mech is the closest hit
pickPoint = hotPoint;
rlPickClass = 1; // BT_RANGE_LOG (issue #4)
++gAimHits;
}
else if (haveStruct)
@@ -4815,6 +4883,7 @@ void
// HudSim part_013.c:5620).
pickTarget = (structOwner != 0) ? structOwner : gBTTerrainEntity;
pickPoint = structPoint;
rlPickClass = 2; // BT_RANGE_LOG (issue #4)
++gAimGround;
}
else
@@ -4831,6 +4900,7 @@ void
{
pickTarget = gBTTerrainEntity;
pickPoint.x = hx; pickPoint.y = hy; pickPoint.z = hz;
rlPickClass = 3; // BT_RANGE_LOG (issue #4)
++gAimGround;
}
}
@@ -4871,12 +4941,44 @@ void
MECH_TARGET_POS(this) = farPt;
targetReticle.targetEntity = gBTTerrainEntity;
targetReticle.rayIntersection = farPt;
rlPickClass = 4; // BT_RANGE_LOG: max-range default (issue #4)
}
else
{
MECH_TARGET_ENTITY(this) = 0; // no world sentinel -> no target
MECH_TARGET_SUBIDX(this) = -1;
}
// [parallax] diag (issue #16): project the pick point back into the
// RENDER view -- the frame the crosshair is drawn in. Reticle +y is
// down, so ry > 0 = the pick lands BELOW the crosshair. A healthy
// boresight holds (rx,ry) ~ (0,0) from max range all the way in; a
// pick ray anchored below the sight line shows ry sliding DOWN
// (positive) as range closes.
if (getenv("BT_PARALLAX_LOG") && pickTarget != 0)
{
static float s_pl = 0.0f; s_pl += dt;
if (s_pl >= 0.5f)
{
s_pl = 0.0f;
extern int BTProjectToRenderView(const float world[3],
float *rx, float *ry, float eyeOut[3]);
float w[3] = { (float)pickPoint.x, (float)pickPoint.y,
(float)pickPoint.z };
float prx = 0.0f, pry = 0.0f, eo[3] = {0,0,0};
if (BTProjectToRenderView(w, &prx, &pry, eo))
{
float pdx = w[0]-eo[0], pdy = w[1]-eo[1], pdz = w[2]-eo[2];
float rng = (float)Sqrt(pdx*pdx + pdy*pdy + pdz*pdz);
DEBUG_STREAM << "[parallax] range=" << rng
<< " rx=" << prx << " ry=" << pry
<< " mech=" << ((hotTarget != 0 && pickTarget == hotTarget) ? 1 : 0)
<< " pick=(" << w[0] << "," << w[1] << "," << w[2] << ")"
<< " eye=(" << eo[0] << "," << eo[1] << "," << eo[2] << ")"
<< "\n" << std::flush;
}
}
}
}
// DETERMINISTIC FIRE-AT-STRUCTURE TEST (BT_FIRE_AT_STRUCT, task #50 verify):
@@ -5052,6 +5154,53 @@ void
BTSetHudTargetRange((Scalar)sShownRange);
}
// BT_RANGE_LOG (Gitea #4 VERDICT instrumentation -- uncommitted diag):
// per-frame trace of the whole range chain. Alongside the pick the
// game actually used, re-cast the SAME ray through (a) the 4m-step
// visual-terrain march (BTGroundRayHit) and (b) an ANALYTIC
// Moller-Trumbore intersect over the same visual triangle set incl.
// walls/ceilings (BTGroundRayHitExact) -- march-vs-exact divergence is
// direct fall-through evidence; exact-vs-structure divergence flags
// visible geometry missing from the static collision tree.
if (getenv("BT_RANGE_LOG"))
{
static float s_rlT = 0.0f; s_rlT += (float)dt;
extern bool BTGroundRayHit(float,float,float, float,float,float,
float, float*,float*,float*);
extern bool BTGroundRayHitExact(float,float,float, float,float,float,
float, float*,float*,float*);
float mhx=0, mhy=0, mhz=0, ehx=0, ehy=0, ehz=0;
float marchD = -1.0f, exactD = -1.0f;
if (rlHaveRay)
{
if (BTGroundRayHit(rlRayS[0], rlRayS[1], rlRayS[2],
rlRayD[0], rlRayD[1], rlRayD[2], 1200.0f, &mhx, &mhy, &mhz))
{
float ddx = mhx-rlRayS[0], ddy = mhy-rlRayS[1], ddz = mhz-rlRayS[2];
marchD = sqrtf(ddx*ddx + ddy*ddy + ddz*ddz);
}
if (BTGroundRayHitExact(rlRayS[0], rlRayS[1], rlRayS[2],
rlRayD[0], rlRayD[1], rlRayD[2], 1200.0f, &ehx, &ehy, &ehz))
{
float ddx = ehx-rlRayS[0], ddy = ehy-rlRayS[1], ddz = ehz-rlRayS[2];
exactD = sqrtf(ddx*ddx + ddy*ddy + ddz*ddz);
}
}
Point3D rp = MECH_TARGET_POS(this);
DEBUG_STREAM << "[range] t=" << s_rlT
<< " true=" << trueRange << " shown=" << sShownRange
<< " cls=" << rlPickClass << " ray=" << rlHaveRay
<< " structD=" << rlStructD
<< " marchD=" << marchD << " exactD=" << exactD
<< " pick=(" << rp.x << "," << rp.y << "," << rp.z << ")"
<< " eye=(" << rlRayS[0] << "," << rlRayS[1] << "," << rlRayS[2] << ")"
<< " dir=(" << rlRayD[0] << "," << rlRayD[1] << "," << rlRayD[2] << ")"
<< " exact=(" << ehx << "," << ehy << "," << ehz << ")"
<< " pos=(" << localOrigin.linearPosition.x << ","
<< localOrigin.linearPosition.y << ","
<< localOrigin.linearPosition.z << ")\n" << std::flush;
}
gBTHudHeading = gDriveHeading; // CompassHeading (attr 0xD)
gBTHudTwist = (float)TorsoHeading(); // RotationOfTorsoHorizontal (attr 4)
gBTHudTwistLimit = (float)GetHorizontalFiringReach();// HorizontalTorsoLimit (attrs 5/6)