Fix drive-to-range stall: BT_GOTO self-drives + steers correctly cross-net
The BT_GOTO beeline never actually closed on a target -- three separate faults: 1. It was gated behind `if(gBTDrive.forced)`, and `forced` was set ONLY by BT_AUTODRIVE -- a chicken-and-egg where the block that would drive was gated on the drive already being on. So BT_GOTO alone just turned in place (the "stall"). Fix: BT_GOTO enables forced mode itself AND supplies its own forward throttle (a beeline must drive, not only steer), cutting the throttle inside a stop radius (weapon range for "enemy", so it holds + shoots). 2. Steering used the gDriveHeading SCALAR MIRROR (seeded to 0), but MP pilots spawn facing a non-zero heading -> it steered the WRONG way. Fix: heading error from the mech's ACTUAL forward (localToWorld's -Z axis), signed angle to the target. 3. The goto turn was routed through controlsMapper->turnDemand, which zeroes out in -net mode (the mapper key-bridge only shapes the local viewpoint mech there) -- the heading froze and A drove straight past B. Fix: apply the goto turn to the orientation integration DIRECTLY (if gBTGotoActive, turn= gBTGotoTurn), after the mapper read. Verified 2-node one-box: A with BT_GOTO="enemy" ALONE (no autodrive) drives dist 1673->231m, holds at weapon range, mechPicks=59, autofire -> B hdlr=192, DESTROYED -- a fully automated human-style cross-pod KILL via the real beam path (not the FORCE_DMG hook). Solo un-regressed (plain autodrive still drives; goto="enemy" resolves the enemy, mechPicks=59, no crash). BT_GOTO_LOG traces the beeline. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
0dfbb97dcc
commit
9353d59eb9
@@ -1356,12 +1356,15 @@ void
|
||||
{
|
||||
static int sAutoFire = -1;
|
||||
static float sAutoDrive = -1.0f;
|
||||
static int sGotoDrive = -1;
|
||||
if (sAutoFire < 0)
|
||||
{
|
||||
const char *af = getenv("BT_AUTOFIRE");
|
||||
sAutoFire = (af && *af == '1') ? 1 : 0;
|
||||
const char *ad = getenv("BT_AUTODRIVE");
|
||||
sAutoDrive = ad ? (float)atof(ad) : 0.0f;
|
||||
const char *gv = getenv("BT_GOTO");
|
||||
sGotoDrive = (gv && *gv) ? 1 : 0;
|
||||
}
|
||||
gBTDrive.fireForced = sAutoFire;
|
||||
if (sAutoDrive > 0.0f)
|
||||
@@ -1369,6 +1372,15 @@ void
|
||||
gBTDrive.forced = 1;
|
||||
gBTDrive.forcedThrottle = sAutoDrive;
|
||||
}
|
||||
else if (sGotoDrive)
|
||||
{
|
||||
// BT_GOTO is self-driving: enable forced mode so the goto beeline
|
||||
// block (gated on gBTDrive.forced, below) runs on its own without
|
||||
// requiring BT_AUTODRIVE. That block sets the actual throttle
|
||||
// (drive toward the target, 0 inside the stop radius).
|
||||
gBTDrive.forced = 1;
|
||||
gBTDrive.forcedThrottle = 0.8f;
|
||||
}
|
||||
}
|
||||
|
||||
if (gBTDrive.allStop) { sLever = 0.0f; sDetent = 0; gBTDrive.allStop = 0; }
|
||||
@@ -1528,19 +1540,56 @@ void
|
||||
{
|
||||
float ddx = s_gx - (float)localOrigin.linearPosition.x;
|
||||
float ddz = s_gz - (float)localOrigin.linearPosition.z;
|
||||
float want = (float)atan2(-ddx, -ddz); // heading with fwd=-Z
|
||||
float err = want - gDriveHeading;
|
||||
while (err > 3.14159265f) err -= 6.2831853f;
|
||||
while (err < -3.14159265f) err += 6.2831853f;
|
||||
float dist2 = ddx * ddx + ddz * ddz;
|
||||
// Heading error from the mech's ACTUAL forward (localToWorld's -Z
|
||||
// axis), NOT the gDriveHeading scalar mirror: that mirror is seeded
|
||||
// to 0, but MP pilots (and any non-zero spawn pose) start facing a
|
||||
// different way, so `want - gDriveHeading` steered the beeline the
|
||||
// WRONG way. err = signed angle (about +Y) from forward to target.
|
||||
UnitVector zAxisG;
|
||||
localToWorld.GetFromAxis(Z_Axis, &zAxisG);
|
||||
float fwdX = -(float)zAxisG.x, fwdZ = -(float)zAxisG.z; // faces -Z
|
||||
float err = 0.0f;
|
||||
float tlen = (float)sqrt((double)dist2);
|
||||
if (tlen > 1e-3f)
|
||||
{
|
||||
float tX = ddx / tlen, tZ = ddz / tlen;
|
||||
float dot = fwdX * tX + fwdZ * tZ;
|
||||
float crs = fwdZ * tX - fwdX * tZ; // signed; flip if it steers away
|
||||
err = (float)atan2((double)crs, (double)dot);
|
||||
}
|
||||
// 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;
|
||||
const float stopDist2 = stopDist * stopDist;
|
||||
const int arrived = (dist2 < stopDist2);
|
||||
// publish for the mapper bridge (the real-controls path reads
|
||||
// gBTDrive in mechmppr.cpp, NOT this local `turn`)
|
||||
gBTGotoTurn = (err > 0.3f) ? 1.0f : (err < -0.3f ? -1.0f
|
||||
: (err > 0.02f ? 0.3f : (err < -0.02f ? -0.3f : 0.0f)));
|
||||
if (ddx * ddx + ddz * ddz < 25.0f) gBTGotoTurn = 0.0f;
|
||||
if (arrived) gBTGotoTurn = 0.0f;
|
||||
// throttle down while far off-heading (the authentic
|
||||
// speed-vs-turn clamp makes run-speed turn circles huge)
|
||||
gBTGotoThrottle = (err > 0.5f || err < -0.5f) ? 0.2f : 1.0f;
|
||||
gBTGotoActive = 1;
|
||||
if (getenv("BT_GOTO_LOG"))
|
||||
{
|
||||
static float s_gl = 0.0f; s_gl += dt;
|
||||
if (s_gl >= 1.0f) { s_gl = 0.0f;
|
||||
DEBUG_STREAM << "[goto] dist=" << (float)sqrt((double)dist2)
|
||||
<< " err=" << err << " turn=" << gBTGotoTurn
|
||||
<< " thr=" << gBTGotoThrottle << " arr=" << arrived
|
||||
<< " fwd=(" << fwdX << "," << fwdZ << ")"
|
||||
<< " tgt=(" << s_gx << "," << s_gz << ")\n" << std::flush; }
|
||||
}
|
||||
// SELF-DRIVE (task #48): a "beeline" harness must supply its OWN
|
||||
// forward throttle, not only steering -- otherwise it just turns
|
||||
// in place and never closes the distance (the "drive-to-range
|
||||
// stall": BT_GOTO alone advanced nothing because the throttle came
|
||||
// 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;
|
||||
turn = gBTGotoTurn; // non-real-controls fallback path
|
||||
}
|
||||
}
|
||||
@@ -1572,6 +1621,14 @@ void
|
||||
// turnDemand is the mode-shaped steering; speedDemand (world u/s, sign =
|
||||
// reverse) feeds the gait target below.
|
||||
turn = controlsMapper->turnDemand;
|
||||
// BT_GOTO steering must reach the orientation integration DIRECTLY: the
|
||||
// mapper round-trip (stickPosition -> turnDemand) zeroes out in -net mode
|
||||
// (the key-bridge only shapes the local viewpoint mech there), which froze
|
||||
// the beeline's heading (it drove straight past the target). The goto turn
|
||||
// is a plain yaw demand -- apply it here, after the mapper read, so it works
|
||||
// identically solo and cross-net.
|
||||
extern int gBTGotoActive; extern float gBTGotoTurn;
|
||||
if (gBTGotoActive) turn = gBTGotoTurn;
|
||||
static float s_mpprLog = 0.0f; s_mpprLog += dt;
|
||||
if (s_mpprLog >= 1.0f)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user