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:
co-authored by
Claude Fable 5
parent
d7158f1f82
commit
70eea6c1a4
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user