#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:
co-authored by
Claude Fable 5
parent
929d6267c2
commit
b00fa39af7
@@ -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;
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 ----------
|
||||
|
||||
Reference in New Issue
Block a user