From ed04ba2d4e20c7ee6821c4133721f82fe9c092bd Mon Sep 17 00:00:00 2001 From: Cyd Date: Mon, 3 Aug 2026 13:41:39 -0500 Subject: [PATCH] BT410 5.3.98: the ground snap -- the mech walks the arena's actual terrain, and there is no gravity The probe half of the master performance's ground model (binary @4aa630-4aa6cc, via BT411's raw-asm decode), placed after Simulate's position integration: place the collision volume; drop a probe from the volume's authored bottom (origin.y + collisionTemplate->minY); ask the zone's box tree what surface lies under it (BoundingBoxTreeNode::FindBoundingBoxUnder); and if the query hits (h > 1e-4), put the origin ON that surface exactly -- origin.y -= (h - lift). The design is worth stating because it is not the obvious one: THERE IS NO GRAVITY ANYWHERE IN THE MECH. Placement is absolute each frame. Walking up-slope rides the lift window (an implicit step allowance), walking off a roof drops instantly, and a probe MISS holds Y -- so a falling-through-the- world runaway is structurally impossible rather than merely guarded against. The engine side needed NOTHING reconstructed: Mover's own ctor already builds collisionTemplate/collisionVolume from the model's BoxedSolidStream resource and the zone tree was live all along. One include (boxsolid.hpp -- mover.hpp only forward-declares BoxedSolid, so the extents were an incomplete type). LIVE, arena run, no fault -- the y track tells the story by itself: y = 20 -> 11.747 -> 10 -> 10 -> 0 -> 19.4049 -> 0 Spawn deck, down a ramp (caught mid-slope), an intermediate level, the arena floor, up onto a structure, and off it. Every previous run had y pinned at its spawn constant for the whole mission. Deferred with the collision increment: frame rejection on blocking hits, the crushable-icon sentinel, the crash clip (SetLegAnimation 0x20) on hard impacts, and the gyro crunch feed. Co-Authored-By: Claude Fable 5 --- restoration/source410/BT/MECH.CPP | 63 +++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/restoration/source410/BT/MECH.CPP b/restoration/source410/BT/MECH.CPP index bb089aee..fbe89f5b 100644 --- a/restoration/source410/BT/MECH.CPP +++ b/restoration/source410/BT/MECH.CPP @@ -53,6 +53,9 @@ #include // Joint / JointSubsystem -- ResolveJoint #include // EntitySegment -- the skeleton segment table #include // Mech::DamageZone -- the hull zone fill (Pass 3) +#if !defined(BOXSOLID_HPP) +# include // BoxedSolid extents -- the ground-snap probe +#endif #include // DamageLookupTable -- the cylinder hit table #include // Player::VehicleDeadMessage -- the death notification #include // BTPlayer::ScoreMessage -- the kill credit @@ -1527,6 +1530,66 @@ void ); localToWorld = localOrigin; + // + //----------------------------------------------------------------------- + // THE GROUND SNAP -- the probe half of the master performance's ground + // model (binary @4aa630-4aa6cc, decoded from raw asm; there is NO + // gravity anywhere in the mech). Place the collision volume, drop a + // probe from the volume's authored bottom, ask the zone's box tree for + // the surface under it, and place the origin ON that surface exactly. + // Walking up-slope rides the lift window; walking off a roof drops + // instantly; a probe MISS (h == -1) holds Y, so a runaway is + // structurally impossible. + // + // The COLLISION half (frame rejection, crush sentinel, crash clip) is a + // separate increment -- it hangs off ProcessCollisionList. + // + // Gated on the engine having built a collision volume for this model + // (Mover's ctor does when the resource carries one); logged once if + // absent so a silent no-op is visible. + //----------------------------------------------------------------------- + // + if ( + GetCollisionVolumeCount() > 0 && + collisionVolume != NULL && + collisionTemplate != NULL + ) + { + MoveCollisionVolume(); + + BoundingBoxTreeNode + *ground_node = GetMoverCollisionRoot(); + if (ground_node != NULL) + { + Point3D + probe = localOrigin.linearPosition; + probe.y += collisionTemplate->minY; + + Scalar + height = -1.0f; + ground_node->FindBoundingBoxUnder(probe, &height); + + if (height > 0.0001f) + { + Scalar + drop = height - collisionTemplate->minY; + localOrigin.linearPosition.y -= drop; + collisionVolume->minY -= drop; + collisionVolume->maxY -= drop; + localToWorld = localOrigin; + } + } + } + else if (getenv("BT_MECH_LOG")) + { + static int noVolumeOnce = 0; + if (!noVolumeOnce++) + { + DEBUG_STREAM << "[ground] mech has NO collision volume -- " + << "snap inactive (model streams none?)" << endl << flush; + } + } + if (getenv("BT_MECH_LOG")) { static Scalar reportAccum = 0.0f;