MP live-play wave: collision economy, missiles, radar transform, panel polarity, comm ticker
The interactive 2-node playtest wave -- every fix decomp-grounded and live-verified: COLLISION ECONOMY (the ram one-shot): StaticBounce mutates worldLinearVelocity per contact and ProcessCollisionList walks EVERY touched solid per frame; with 2007 terrain-as-solids the reflections compounded x4-x40 within one frame and a walking bump one-shot a pristine mech for 112,375 pts (62-pt authentic economy). Fix: frameEntryWorldVelocity restore per contact (damage always priced at the real approach speed -- all the binary's physics ever saw); Mech::Reset zeroes the mover motion (respawn = teleport); [collide-tx]/[mp-hdlr] telemetry. Gotcha #16 (engine-facility drift class). MISSILES: peer-visible salvos (the launcher record extension carries a salvo counter + aim point; ForceUpdate actually enqueues it -- the dirty flag alone never serialized), the authentic arc (authored MuzzleVelocity vector + the Seeker's 200m/0.1/300 loft + gain-4 steering, decoded from @004beae4/@004bef78), world-impact bursts (rounds detonate on cave geometry instead of phasing through), contact-only damage (flight-cap expiry = fizzle, no more teleport damage), live re-lead, and ballistic (unguided) shells for autocannons. projweap's stale BTPushProjectile extern (the /FORCE signature trap, gotcha #6 corollary) crashed the Avatar's first AFC100 shot -- fixed + sweep rule recorded. RADAR: two transcription bugs made the scope permanently empty -- FUN_0040b244 is the affine INVERSE (not a copy) and FUN_0040adec writes ONLY the 3x3 rotation (never the translation); worldToView now Invert(view) built rotation-first. CulturalIcons sorted out of the moving grid (the phantom red pips were map props), visible-radius culls on all three draw passes, live pip verified at |delta| x ppm px. Gotcha #17 (verify the FUN_ body, not its call shape). WEAPON PANELS (the frozen-dial hunt): the binary's *(subsystem+0x40) means FAILED -- the recon's 'operating' name was backwards, inverting the destroyed-X lamps, the panel look, the children enable and the ready-lamp gate (which had NEVER executed). Polarity chain corrected end-to-end (failedState, fed by real damage saturation). Root cause of the freezes: MFD page-mode gating -- the dev composite shows ALL pages at once, so off-page dials legitimately stopped; under BT_DEV_GAUGES the 15 page-plane bits stay active (the exclusive secondary trio untouched). The SEH gauge guard now names its kills; repaint-heal resets the incremental arc after panel repaints; [panel]/[arc] probes added. COMM/SCORE: MessageBoard LIVE (the engine already shipped the whole Player__StatusMessage queue; wired the binary's one producer -- the kill branch, victim's name, 6s -- plus the consumer bridge and a lazy source bind); MP DEATHS counted via the observed-death tally (each node scores every pilot from locally observed events, the same model as the KILLS credit) and the -2/-1 engine seed clamped for display. DEV UX: node-tagged window titles (-net port), gauge panel reworked (1320x480, true 4:3 MFD cells, the portrait secondary UNROTATED upright, linear filtering, BT_GAUGE_SCALE), fixed close spawns via BT_SPAWN_XZ, Boreas flies an Avatar (first second-chassis live outing). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
dd27238ceb
commit
bb795e2805
@@ -68,8 +68,49 @@
|
||||
// WAVE 7 Phase B -- the port-side flying-projectile service (defined in mech4.cpp, a
|
||||
// complete-Mech TU with the render + damage path). A fired missile becomes a tracked
|
||||
// projectile that flies to the target + delivers this weapon's damage on impact.
|
||||
// Missile-arc wave: launch_velocity = the authored MuzzleVelocity VECTOR in the
|
||||
// shooter's frame (up-tilted rack launch); damage <= 0 = a VISUAL-ONLY round.
|
||||
extern void BTPushProjectile(const Point3D &muzzle, void *shooter, void *target,
|
||||
const Point3D &targetPos, Scalar speed, Scalar damage);
|
||||
const Point3D &targetPos, Scalar speed, Scalar damage,
|
||||
const Vector3D *launch_velocity = 0, int guided = 1);
|
||||
|
||||
//###########################################################################
|
||||
// Port-side salvo replication state (missile-visibility wave).
|
||||
//
|
||||
// The launcher OBJECT is byte-locked at 0x44C (factory alloc) -- salvo state
|
||||
// cannot live on it. Keyed by launcher identity: on a MASTER, `fired` counts
|
||||
// FireWeapon salvos (serialized in the extended update record); on a
|
||||
// REPLICANT, `seen` tracks the last counter applied (-1 = unsynced, so a
|
||||
// freshly-joined peer does not replay the master's historical count).
|
||||
//###########################################################################
|
||||
namespace {
|
||||
struct BTSalvoState
|
||||
{
|
||||
const void *owner;
|
||||
int fired; // master: salvos launched
|
||||
int seen; // replicant: last counter applied (-1 = unsynced)
|
||||
Point3D target; // master: the last salvo's aim point
|
||||
};
|
||||
BTSalvoState gSalvoTable[64];
|
||||
|
||||
BTSalvoState &BTSalvoOf(const void *launcher)
|
||||
{
|
||||
int freeSlot = -1;
|
||||
for (int i = 0; i < 64; ++i)
|
||||
{
|
||||
if (gSalvoTable[i].owner == launcher)
|
||||
return gSalvoTable[i];
|
||||
if (gSalvoTable[i].owner == 0 && freeSlot < 0)
|
||||
freeSlot = i;
|
||||
}
|
||||
BTSalvoState &s = gSalvoTable[(freeSlot >= 0) ? freeSlot : 0];
|
||||
s.owner = launcher;
|
||||
s.fired = 0;
|
||||
s.seen = -1;
|
||||
s.target = Point3D(0.0f, 0.0f, 0.0f);
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
//###########################################################################
|
||||
//###########################################################################
|
||||
@@ -258,15 +299,91 @@ void MissileLauncher::FireWeapon()
|
||||
+ launchVelocity.y*launchVelocity.y
|
||||
+ launchVelocity.z*launchVelocity.z);
|
||||
// Always launch -- BTPushProjectile falls back to gEnemyMech when the owner's target
|
||||
// slot isn't populated (bring-up); missileCount missiles per salvo.
|
||||
// slot isn't populated (bring-up); missileCount missiles per salvo. The authored
|
||||
// MuzzleVelocity VECTOR rides along (missile-arc wave): the round leaves the rack
|
||||
// on the authored up-tilt, then the seeker loft + steering arc it onto the target.
|
||||
int nmiss = (missileCount > 0 && missileCount < 40) ? missileCount : 1;
|
||||
for (int i = 0; i < nmiss; ++i)
|
||||
BTPushProjectile(muzzle, o, target, targetPos, speed, damageData.damageAmount);
|
||||
BTPushProjectile(muzzle, o, target, targetPos, speed, damageData.damageAmount,
|
||||
&launchVelocity);
|
||||
|
||||
// Salvo replication (missile-visibility wave): bump the fire counter + stamp
|
||||
// the aim point; the extended update record carries both to peer nodes.
|
||||
{
|
||||
BTSalvoState &s = BTSalvoOf(this);
|
||||
++s.fired;
|
||||
s.target = targetPos;
|
||||
}
|
||||
|
||||
simulationFlags |= 0x1; // replication-dirty
|
||||
// ENQUEUE the record (visibility fix): the dirty flag alone never
|
||||
// serializes -- ForceUpdate sets the updateModel bit that makes the next
|
||||
// PerformAndWatch write THIS subsystem's record into the mech's update
|
||||
// stream (the exact mechanism the Emitter's beam replication uses,
|
||||
// emitter.cpp:512). Without it the peer never saw a salvo record at all.
|
||||
ForceUpdate();
|
||||
Check_Fpu();
|
||||
}
|
||||
|
||||
//#############################################################################
|
||||
// WriteUpdateRecord / ReadUpdateRecord (PORT record extension -- missile-
|
||||
// visibility wave; see the mislanch.hpp banner). The master serializes the
|
||||
// salvo counter + aim point after the MechWeapon base fields; the replicant
|
||||
// edge-detects the counter and mirrors the salvo as VISUAL-ONLY rounds
|
||||
// (damage 0 -- the master's own rounds deliver the damage cross-pod).
|
||||
//
|
||||
void
|
||||
MissileLauncher::WriteUpdateRecord(Simulation__UpdateRecord *message, int update_model)
|
||||
{
|
||||
MechWeapon::WriteUpdateRecord(message, update_model); // @004b9690 (alarm fields)
|
||||
|
||||
MissileLauncher__UpdateRecord *rec = (MissileLauncher__UpdateRecord *)message;
|
||||
BTSalvoState &s = BTSalvoOf(this);
|
||||
rec->recordLength = sizeof(MissileLauncher__UpdateRecord);
|
||||
rec->salvoCounter = s.fired;
|
||||
rec->salvoRounds = missileCount;
|
||||
rec->salvoTarget = s.target;
|
||||
}
|
||||
|
||||
void
|
||||
MissileLauncher::ReadUpdateRecord(Simulation__UpdateRecord *message)
|
||||
{
|
||||
MechWeapon::ReadUpdateRecord(message); // @004b964c (alarm apply)
|
||||
|
||||
MissileLauncher__UpdateRecord *rec = (MissileLauncher__UpdateRecord *)message;
|
||||
BTSalvoState &s = BTSalvoOf(this);
|
||||
if (s.seen < 0)
|
||||
{
|
||||
// first sync after spawn/join: adopt the master's count silently --
|
||||
// no replay of salvos fired before we could see them.
|
||||
s.seen = rec->salvoCounter;
|
||||
}
|
||||
else if (rec->salvoCounter != s.seen)
|
||||
{
|
||||
s.seen = rec->salvoCounter;
|
||||
|
||||
// Mirror ONE salvo (a missed record collapses to one -- catch-up
|
||||
// replays would draw stale phantom volleys). Rounds fly the same
|
||||
// authored launch vector + seeker-loft arc as the master's, from
|
||||
// this replicant's own resolved rack ports, damage 0.
|
||||
Point3D mz;
|
||||
GetMuzzlePoint(mz); // @004b9948
|
||||
Scalar spd = (Scalar)sqrtf(launchVelocity.x*launchVelocity.x
|
||||
+ launchVelocity.y*launchVelocity.y
|
||||
+ launchVelocity.z*launchVelocity.z);
|
||||
int n = rec->salvoRounds;
|
||||
if (n < 1 || n > 40)
|
||||
n = (missileCount > 0 && missileCount < 40) ? missileCount : 1;
|
||||
for (int i = 0; i < n; ++i)
|
||||
BTPushProjectile(mz, owner, 0 /*no entity: aim point only*/,
|
||||
rec->salvoTarget, spd, 0.0f /*VISUAL*/, &launchVelocity);
|
||||
if (getenv("BT_PROJ_LOG"))
|
||||
DEBUG_STREAM << "[projectile] REPLICANT salvo x" << n
|
||||
<< " at(" << rec->salvoTarget.x << "," << rec->salvoTarget.y
|
||||
<< "," << rec->salvoTarget.z << ")\n" << std::flush;
|
||||
}
|
||||
}
|
||||
|
||||
//#############################################################################
|
||||
// TakeDamage / DeathReset
|
||||
//
|
||||
|
||||
Reference in New Issue
Block a user