BT410 5.3.99: walls are real -- frame rejection, crash self-damage, the knockdown stagger, and a pricing question left honestly open

The response half of the ground model (binary @4aa6cf-4aab0b), after the
5.3.98 snap:

  WALLS BLOCK BY FULL FRAME REJECTION, never slide or climb: any blocking
  contact restores the start-of-frame position and zeroes the velocity.

  THE CRASH HURTS: the accumulated collision Damage is dispatched at our own
  mech, zone -1 -- through the same cylinder lottery as enemy fire -- which
  is what makes wall-grinding self-limiting in the original.

  A HARD HIT STAGGERS: past the binary's |v|^2 > 40 threshold (@0x4ab184),
  both gait channels bind the bump clip (slot 0x20, the reason the clip
  array outgrows the name table) and recover to Standing at its end.  Both
  channels together, per the donor's foot-slip finding: staggering only the
  leg channel desyncs the two cycles permanently.

LIVE, one arena run, no fault -- and the run turned into an unplanned
full-system exercise: 8 wall contacts (three at knockdown force, iv2
745-932; one low-speed bump correctly blocked without stagger), and the
self-damage cascaded through vital zones to MECH KILL, pilot notification,
respawn HEAL-and-place, four times over.  The death and respawn machinery
all held.

OPEN, stated rather than tuned away: THE PRICE IS TOO HOT.  Wall crashes
land 14k-24k damage points against a game where a PPC is ~12 and an upper
torso carries ~124 -- a hard crash one-shots vital zones through the
lottery.  BT411's economy audit hit the identical symptom ("tapped a wall
and died instantly") and traced part of it to compounded StaticBounce
reflections across multi-solid frames.  The fix lives in the
Mech::ProcessCollision override (with the crushable-icon sentinel and the
gyro crunch), which is the NEXT increment -- the pricing stays as the
engine computes it until that override is reconstructed from the binary,
because guessing a divisor would be tuning, not archaeology.  The [crash]
log line prints amount + iv2 under BT_MECH_LOG as the audit trail.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Cyd
2026-08-03 13:55:47 -05:00
co-authored by Claude Fable 5
parent ed04ba2d4e
commit 4ace2571d9
+91
View File
@@ -1523,6 +1523,9 @@ void
// Integrate position and commit the Origin to the world transform.
//-----------------------------------------------------------------------
//
Point3D
frame_start_position = localOrigin.linearPosition;
localOrigin.linearPosition.AddScaled(
localOrigin.linearPosition,
worldLinearVelocity,
@@ -1590,6 +1593,94 @@ void
}
}
//
//-----------------------------------------------------------------------
// COLLISIONS -- the response half of the master performance's ground
// model (binary @4aa6cf-4aab0b). Walls block by FULL FRAME REJECTION,
// never by slide or climb: any blocking contact restores the
// start-of-frame position and zeroes the velocity. The crash itself
// HURTS -- the accumulated collision Damage is dispatched at OUR OWN
// mech, zone -1 (the cylinder lottery), which is what makes wall-
// grinding self-limiting. And a hard enough hit (|v|^2 > 40, the
// binary's @0x4ab184 threshold) staggers the mech: both gait channels
// bind the bump clip (slot 0x20) and recover to Standing at its end.
//
// Guarded on the collision assistant: the engine's GetCurrentCollisions
// walks it unchecked, and only the viewpoint mech gets one
// (StartCollisionAssistant, BTL4APP.CPP:409).
//
// STAGED deltas, named: the crushable-CulturalIcon sentinel (0.00123f
// -- the move stands, gyro crunch) needs the Mech::ProcessCollision
// override, a later increment; the gyro crunch feed itself is with it;
// and the collisionState audio push waits on the same override's
// contact accumulation.
//-----------------------------------------------------------------------
//
if (
collisionAssistant != NULL &&
GetCollisionVolumeCount() > 0 &&
collisionVolume != NULL
)
{
Vector3D
impact_velocity = worldLinearVelocity;
BoxedSolidCollisionList
*collisions = GetCurrentCollisions();
Damage
collision_damage;
if (collisions != NULL)
{
ProcessCollisionList(
collisions, time_slice, frame_start_position,
&collision_damage);
}
if (collision_damage.damageAmount > 0.0f)
{
worldLinearVelocity = Vector3D(0.0f, 0.0f, 0.0f);
localOrigin.linearPosition = frame_start_position;
localToWorld = localOrigin;
MoveCollisionVolume();
Entity::TakeDamageMessage
crash(
Entity::TakeDamageMessageID,
sizeof(Entity::TakeDamageMessage),
GetEntityID(),
-1,
collision_damage
);
Dispatch(&crash);
Scalar
impact_squared =
impact_velocity.x * impact_velocity.x +
impact_velocity.y * impact_velocity.y +
impact_velocity.z * impact_velocity.z;
if (
impact_squared > 40.0f &&
animationClips[0x20] != ResourceDescription::NullResourceID &&
legStateAlarm.GetLevel() != 0x20
)
{
SetLegAnimation(0x20);
SetBodyAnimation(0x20);
ForceUpdate(1);
ForceUpdate(0x20);
}
if (getenv("BT_MECH_LOG"))
{
DEBUG_STREAM << "[crash] BLOCK dmg="
<< collision_damage.damageAmount
<< " iv2=" << impact_squared
<< (impact_squared > 40.0f ? " KNOCKDOWN" : "")
<< endl << flush;
}
}
}
if (getenv("BT_MECH_LOG"))
{
static Scalar reportAccum = 0.0f;