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
@@ -679,15 +679,23 @@ void
|
||||
if (currentPositionPointer != NULL)
|
||||
{
|
||||
Point3D center = *currentPositionPointer; // FUN_00408440(local_5c, ...)
|
||||
view.SetFromAxis(W_Axis, center); // FUN_0040b0ac(view,3,center)
|
||||
|
||||
// TRANSCRIPTION FIX #2 (the invisible-pip bug): FUN_0040adec writes
|
||||
// ONLY the 3x3 rotation elements ([0..2],[4..6],[8..10]) -- it never
|
||||
// touches the translation row. The binary therefore ends with
|
||||
// view = [R(q) | center]: rotation SET, center PRESERVED. Our old
|
||||
// `view *= yaw` was the engine's full COMPOSE, which also rotated the
|
||||
// translation (view = [R | center x R]) -- the subsequent Invert then
|
||||
// mapped center x R to the origin instead of the viewer, so every
|
||||
// blip landed hundreds of pixels off-scope. Rotation first
|
||||
// (rotation-only assignment), translation row LAST.
|
||||
if (currentAngularPointer != NULL)
|
||||
{
|
||||
if (!rockAndRoll)
|
||||
{
|
||||
//
|
||||
// Top-down: keep heading only. Extract the yaw, build a
|
||||
// yaw-only quaternion and concatenate it.
|
||||
// yaw-only quaternion, SET the rotation block from it.
|
||||
//
|
||||
EulerAngles euler;
|
||||
euler = *currentAngularPointer; // FUN_0040954c
|
||||
@@ -695,21 +703,27 @@ void
|
||||
SinCosPair sc;
|
||||
sc = Radian(heading); // FUN_00408328
|
||||
Quaternion yaw(0.0f, sc.sine, 0.0f, sc.cosine); // FUN_00409948
|
||||
view *= yaw; // FUN_0040adec
|
||||
view = yaw; // rotation-only (FUN_0040adec semantics)
|
||||
}
|
||||
else
|
||||
{
|
||||
//
|
||||
// "Rock and roll": use the full orientation matrix.
|
||||
// "Rock and roll": use the full orientation.
|
||||
//
|
||||
AffineMatrix full;
|
||||
full = *currentAngularPointer; // FUN_00409968
|
||||
view *= full; // FUN_0040adec
|
||||
view = *currentAngularPointer; // rotation-only build
|
||||
}
|
||||
}
|
||||
view.SetFromAxis(W_Axis, center); // FUN_0040b0ac(view,3,center) -- translation LAST
|
||||
}
|
||||
|
||||
worldToView = view; // FUN_0040b244(this+0x324, view)
|
||||
// TRANSCRIPTION FIX (the invisible-pip bug, 2026-07-12): FUN_0040b244 is
|
||||
// the full affine INVERSE (cofactor expansion + determinant divide,
|
||||
// part_001.c:172), not a copy. `view` above is the OPERATOR's local->
|
||||
// world pose (translate to the mech + yaw); the radar needs its inverse
|
||||
// -- world->scope, center subtracted, heading unrotated. Read as a copy,
|
||||
// every pip/name transformed to (world + center) x ppm = thousands of
|
||||
// pixels off-scope: the scope drew empty at all times.
|
||||
worldToView.Invert(view); // FUN_0040b244(this+0x324, view)
|
||||
|
||||
//
|
||||
// Bake the metres->pixels scale into the matrix.
|
||||
@@ -717,6 +731,18 @@ void
|
||||
Vector3D scale;
|
||||
scale.x = scale.y = scale.z = pixelsPerMeter;
|
||||
worldToView.Multiply(worldToView, scale); // FUN_0040b374
|
||||
if (getenv("BT_MAP_LOG"))
|
||||
{
|
||||
static int s_m = 0;
|
||||
if ((s_m++ % 30) == 0)
|
||||
DEBUG_STREAM << "[map] w2v ppm=" << pixelsPerMeter
|
||||
<< " halfW=" << halfWidth << " scale=" << currentScale
|
||||
<< " m=[" << worldToView(0,0) << "," << worldToView(0,1) << "," << worldToView(0,2)
|
||||
<< " | " << worldToView(1,0) << "," << worldToView(1,1) << "," << worldToView(1,2)
|
||||
<< " | " << worldToView(2,0) << "," << worldToView(2,1) << "," << worldToView(2,2)
|
||||
<< " | t " << worldToView(3,0) << "," << worldToView(3,1) << "," << worldToView(3,2)
|
||||
<< "]\n" << std::flush;
|
||||
}
|
||||
|
||||
BuildRadarShadow(worldToView); // FUN_004c228c
|
||||
|
||||
@@ -803,6 +829,14 @@ void
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// VISIBLE-RADIUS cull (port): the capability radius above (RadarPercent
|
||||
// x 4000 m) exceeds the SCOPE radius (currentScale/2 at the current
|
||||
// zoom); an out-of-scope draw lands beyond the port rect on the shared
|
||||
// gauge surface. Skip what the scope can't show.
|
||||
if (delta.x*delta.x + delta.z*delta.z > currentScale * currentScale * 0.25f)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Pip *pip = (pips != NULL)
|
||||
? pips->LookUpPip(entity->GetResourceID()) // FUN_004688ce(pips+0x58,entity+0x1bc,0)
|
||||
@@ -888,10 +922,23 @@ void
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// VISIBLE-RADIUS cull (port; see DrawStatic): don't draw past the scope.
|
||||
if (delta.x*delta.x + delta.z*delta.z > currentScale * currentScale * 0.25f)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Pip *pip = (pips != NULL)
|
||||
? pips->LookUpPip(entity->GetResourceID())
|
||||
: NULL;
|
||||
if (mlog)
|
||||
{
|
||||
Point3D sp;
|
||||
sp.Multiply(EntityPosition(entity), worldToView);
|
||||
DEBUG_STREAM << "[map] pip=" << (void*)pip
|
||||
<< " screen=(" << sp.x << "," << sp.y << "," << sp.z << ")"
|
||||
<< " lod=" << LODIndex << " mpp=" << metersPerPixel << "\n" << std::flush;
|
||||
}
|
||||
if (pip != NULL)
|
||||
{
|
||||
AffineMatrix blipXform;
|
||||
@@ -987,6 +1034,15 @@ void
|
||||
// "MapDisplay::DrawNames -- GetSize() / iterator mismatch"
|
||||
// "d:\\tesla\\bt\\bt_l4\\BTL4RDR.CPP", 0x408
|
||||
}
|
||||
else
|
||||
{
|
||||
// VISIBLE-RADIUS cull (port; see DrawStatic): a label for a
|
||||
// contact beyond the scope would blit outside the port rect.
|
||||
Vector3D d;
|
||||
d.Subtract(EntityPosition(entity), viewingPosition);
|
||||
if (d.x*d.x + d.z*d.z > currentScale * currentScale * 0.25f)
|
||||
entity = NULL; // extract guards NULL -> no label
|
||||
}
|
||||
nameArray[i].ExtractFromEntity(entity, worldToView, owner, target); // FUN_004c19fc
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user