#83 FIXED: the incoming-missile alarm sounds again -- the July F7 driver was wired into DEAD CODE. Root cause: the alarm feed (cc2b109) lived in Missile::MoveAndCollide, but the world-entity Missile is a BLOCKED TRANSCRIPTION (2007 Entity-base mismatch) that never flies; live flight is the BTPushProjectile simulator in mech4.cpp -- so IncomingLock/DistanceToMissile stayed 0/FLT_MAX in every field build (solo AND MP) while the doc header said F7 fixed. Fix: the feed moves to the push simulator's per-frame update -- salvo LEAD only (the port's cluster = the binary's ONE Missile per trigger, @004bf5b4 ctor-registration into target+0x418 read by the target tick @004a9b5c: lock=1, dist=|missile-mech| entity positions); mirror leads on peer nodes report identically (mislanch resolves the live target by replicated ID), which is what puts the alarm in the VICTIM's cockpit -- inbound missiles are always mirrors there. Bridge hardened: BTIsRegisteredMech class check (the binary's 0x50bdb4 check) + the port's latched-death predicate 2||9 (@0049fb54). New: [incoming] receipt (BT_INCOMING_LOG, edges + 1Hz held) and BT_DESIGNATE=enemy (bench designator: production designation slots stamped with the nearest live mech on no-mech frames only -- standoff locks the boresight pick can't hold). Transcription block kept in missile.cpp with an INERT banner so the trap can't re-bite. Bench (2-node standoff LRM duel, 700-800u): 54/55 lock edges per node, dist closing 792->87 over ~2.2s, beeper STARTs with LIVE authored tempo (10 far -> 435 close, the 100..800->600..10 map) -- on replicant mirrors, i.e. the MP case. Era witness: Oracle identified [1:74] IncomingAlarm01 n58 on the soundboard same day ('sped up in the pods').

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016bw71WVsccjwW7uKRg4aWD
This commit is contained in:
Joe DiPrima
2026-08-13 22:25:38 -05:00
co-authored by Claude Fable 5
parent 929d6267c2
commit b00fa39af7
3 changed files with 129 additions and 19 deletions
+8 -3
View File
@@ -1019,9 +1019,14 @@ protected:
public:
int MovementMode() { return (int)GetSimulationState(); }
void SetMovementMode(int m) { SetSimulationState((unsigned)m); }
// (AUDIO_FIDELITY F7) called by each homing Missile per MoveAndCollide
// tick; PerformAndWatch latches the accumulators into the published
// IncomingLock/DistanceToMissile attributes each frame.
// (AUDIO_FIDELITY F7 / #83) called by each inbound Missile per
// MoveAndCollide tick; PerformAndWatch latches the accumulators into
// the published IncomingLock/DistanceToMissile attributes each frame.
// [T3 micro-semantic] the binary (@004a9b5c) reads the FIRST member
// of the mech's inbound socket list, not the minimum; the list's
// insertion order is unresolved (Node/Plug layer), so we keep MIN --
// observable only with 2+ simultaneous inbound missiles, where min
// = nearest = fastest beep (the conservative reading).
void ReportIncomingMissile(Scalar range)
{
incomingLockNext = 1;
+100 -8
View File
@@ -1793,6 +1793,23 @@ static void
p.target = 0; // fuze disarmed with the lock
}
}
// (#83/F7) THE INCOMING-MISSILE ALARM FEED lives HERE, in the live
// flight simulator -- NOT in missile.cpp (the world-entity Missile is
// a blocked transcription that never flies; the July F7 driver rode
// it, which is why the alarm stayed silent in every field build).
// Binary contract: ONE cluster Missile per trigger registers into its
// target mech's inbound socket list at ctor (@004bf5b4 tail) and the
// TARGET's tick reads it (@004a9b5c: lock=1, dist=|missile-mech|).
// The salvo LEAD is the port's cluster (task #62), so lead-only =
// one report per salvo = the binary's one registration; mirror leads
// on peer nodes report identically (mislanch.cpp resolves the live
// target by replicated ID), which is what puts the alarm in the
// VICTIM's cockpit -- inbound missiles are always mirrors there.
if (p.guided && p.salvoLead && p.target != 0)
{
extern void BTReportIncomingMissile(Entity *target, const Point3D &missile_position);
BTReportIncomingMissile((Entity *)p.target, p.pos);
}
if (p.guided)
{
Point3D aim = p.targetPos;
@@ -6588,12 +6605,59 @@ void
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;
}
// BENCH DESIGNATOR (#83 rig, env-gated, off by default):
// BT_DESIGNATE=enemy overrides the frame's designation with the
// nearest live mech whenever the boresight pick found NO mech --
// a standoff missile duel needs locks at ranges where the enemy
// subtends too small an angle for the self-driven aim to hold.
// A real mech pick still wins (this fills no-mech frames only),
// and the stamped state is exactly the production designation
// (entity slot + aim point + zone -1), so everything downstream
// -- fire gates, seeker, incoming-alarm feed -- runs authentic.
// The HUD reticle is left alone (no fake lock ring).
{
static int s_desig = -1;
if (s_desig < 0)
{
const char *dv = getenv("BT_DESIGNATE");
s_desig = (dv != 0 && !stricmp(dv, "enemy")) ? 1 : 0;
}
extern int BTIsRegisteredMech(Entity *e);
if (s_desig == 1
&& (pickTarget == 0 || !BTIsRegisteredMech(pickTarget)))
{
extern int BTGetTargetCandidates(Entity *shooter, Entity **out, int maxOut);
Entity *ec[32];
const int enc = BTGetTargetCandidates((Entity *)this, ec, 32);
float best = 1e30f;
Mech *bm = 0;
for (int ei = 0; ei < enc; ++ei)
{
Mech *em = (Mech *)ec[ei];
if (em == 0 || em->IsMechDestroyed()) continue;
float edx = (float)em->localOrigin.linearPosition.x
- (float)localOrigin.linearPosition.x;
float edz = (float)em->localOrigin.linearPosition.z
- (float)localOrigin.linearPosition.z;
float ed2 = edx*edx + edz*edz;
if (ed2 < best) { best = ed2; bm = em; }
}
if (bm != 0)
{
MECH_TARGET_ENTITY(this) = (Entity *)bm;
MECH_TARGET_SUBIDX(this) = -1;
MECH_TARGET_POS(this) = bm->localOrigin.linearPosition;
}
}
}
// [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
@@ -8868,6 +8932,20 @@ void
// Missile::MoveAndCollide since our last frame, then re-arm. The authored
// beeper matches incomingLock 1/0 and the tempo scale reads the range --
// both polled by ExecuteWatchers below.
// (#83 receipt) BT_INCOMING_LOG: lock edges always; ~1Hz while locked.
if (getenv("BT_INCOMING_LOG"))
{
static int s_inCount = 0;
if ((incomingLock == 0) != (incomingLockNext == 0))
DEBUG_STREAM << "[incoming] " << (incomingLockNext ? "LOCK" : "clear")
<< " mech=" << (void *)this
<< " dist=" << (float)distanceToMissileNext
<< " t=" << (GetTickCount() % 1000000) << "\n" << std::flush;
else if (incomingLockNext != 0 && (s_inCount++ % 60) == 0)
DEBUG_STREAM << "[incoming] held mech=" << (void *)this
<< " dist=" << (float)distanceToMissileNext
<< " t=" << (GetTickCount() % 1000000) << "\n" << std::flush;
}
incomingLock = incomingLockNext;
distanceToMissile = distanceToMissileNext;
incomingLockNext = 0;
@@ -9720,18 +9798,32 @@ void
}
//###########################################################################
// BTReportIncomingMissile -- complete-type bridge (F7)
// BTReportIncomingMissile -- complete-type bridge (F7/#83)
//
// Missile::MoveAndCollide (missile.cpp, forward-declared Mech only) reports
// its target + range each tick; the missile-alarm attributes latch in
// Mech::PerformAndWatch. Seeker targets are always mechs in this game
// (the launcher's lock); null-guarded.
// its CONSTRUCTION target each tick; the missile-alarm attributes latch in
// Mech::PerformAndWatch. Binary contract (@004bf5b4 ctor registration +
// @004a9b5c target-tick read): the ctor class-checks the target as a Mech
// (vs 0x50bdb4) before the socket Add -- seeker targets are always mechs
// in this game; distance = |missilePos - mechPos| from the ENTITY
// positions (not the seeker lead); a dead target's inbound list is
// emptied on the death path (states 9/10), mirrored here as a skip.
//###########################################################################
void BTReportIncomingMissile(Entity *target, Scalar range)
void BTReportIncomingMissile(Entity *target, const Point3D &missile_position)
{
if (target != 0)
{
((Mech *)target)->ReportIncomingMissile(range);
extern int BTIsRegisteredMech(Entity *e);
if (target != 0 && BTIsRegisteredMech(target)) // the binary class-checks the
{ // ctor target vs Mech (0x50bdb4)
Mech *mech = (Mech *)target;
const int mm = mech->MovementMode();
if (mm == 2 || mm == 9) // the port's latched death modes
return; // (@0049fb54 predicate) -- the
// binary empties the inbound list
// on the death path
Vector3D to_missile;
to_missile.Subtract(missile_position,
mech->localOrigin.linearPosition);
mech->ReportIncomingMissile(to_missile.Length());
}
}
+21 -8
View File
@@ -235,15 +235,28 @@ void Missile::MoveAndCollide(Scalar time_slice)
if (thruster != 0)
thruster->MissileThrusterSimulation(time_slice); // bleed burnTimeRemaining
// (AUDIO_FIDELITY F7) report the threat to the homing target: the mech's
// IncomingLock/DistanceToMissile attributes (binary @0x3fc/0x400) drive
// the authored missile-alarm beeper whose TEMPO rises as we close.
if (seeker != 0 && seeker->targetEntity != 0 && (simulationFlags & 1) == 0)
// (AUDIO_FIDELITY F7 / #83) *** TRANSCRIPTION ONLY -- THIS NEVER RUNS ***
// The world-entity Missile is blocked (2007 engine Entity-base mismatch);
// live flight is the BTPushProjectile simulator, and the WORKING alarm
// feed is there (mech4.cpp, the salvo-lead report in the push update
// loop). The July F7 driver lived only here -- a month of field silence
// (#83) because the reporter rode a class that never flies. Kept for
// the day the entity unblocks:
// report the threat to the CONSTRUCTION target,
// not the seeker's live target: the binary registers the missile into the
// target mech's inbound socket list in the ctor tail (@004bf5b4:
// this+0x34c class-checked as Mech, then target+0x418 list Add) and the
// TARGET'S OWN tick (@004a9b5c) reads that list -- IncomingLock=1 +
// DistanceToMissile = |missilePos - mechPos| (entity positions, NOT the
// seeker lead point). Registration survives a seeker lock-drop; it ends
// only with the missile (socket weak-ref) or the target's death (the
// death path walks + empties the list -- the bridge mirrors that with a
// dead-target skip). The old seeker-gated lead-point report understated
// range and went silent on lock-drop.
if (targetConnection != 0 && (simulationFlags & 1) == 0)
{
extern void BTReportIncomingMissile(Entity *target, Scalar range);
Vector3D to_target;
to_target.Subtract(seeker->targetPosition, localOrigin.linearPosition);
BTReportIncomingMissile(seeker->targetEntity, to_target.Length());
extern void BTReportIncomingMissile(Entity *target, const Point3D &missile_position);
BTReportIncomingMissile((Entity *)targetConnection, localOrigin.linearPosition);
}
// --- guidance: build steering toward the Seeker's lead point ----------