BT410 5.3.100: the collision economy -- armor is never touched by a crash, and the wall stops one-shotting the mech

The 5.3.99 open question is closed the way the binary closes it, not with a
divisor.  Two pieces:

THE DIVERT (damage hub @0x4a0368): type-0 Collision damage NEVER reaches the
armor zones.  Our handler was routing wall crashes through the same
cylinder-lottery path as enemy fire, which is why 24k-point kinetic numbers
one-shot vital zones.  The binary short-circuits them into

THE DISTRIBUTOR (@0049ffcc): the manual's "collision damage" technician
setting.  Gated on the owning player's advanced-damage flag; priced against
a 100 km/h reference impact --

    scale  = (2000 / mass) / (100 km/h)^2 / (1 - elasticity^2)
    amount = raw * scale;   under 0.5 the tap is FREE
    n      = Round(amount * 2) sub-hits of amount/n each

-- and applied as rattle crits to random INTERNAL subsystems (HeatSink /
Gyroscope / Torso families only), drawn by cumulative
collisionCriticalHitWeight against a [0,1) roll.  An un-won roll lands
nowhere; the binary does not normalize the weights, so neither do we.

LIVE, same arena mission as 5.3.99's four-death run:

    [colldmg] rattle 0.677919 pts in 1 sub-hits (scale=3.6e-05)
    [crash] BLOCK dmg=18831.1 iv2=711.428 KNOCKDOWN
    [crash] BLOCK dmg=331.878 iv2=12.549        (x6, pressed against a wall)
    deaths: ZERO (was four)

An 18.8k-point crash becomes 0.68 points of internal rattle; the low-speed
wall grind prices as free taps under the 0.5 floor; the knockdown stagger
still fires above the velocity threshold.  Walls now block, stagger, and
rattle -- they no longer execute.

Plumbing: MechSubsystem::CollisionCritWeight() accessor (the weight was
already streamed at +0x10C, protected), Mech::DistributeCollisionDamage
declared, random.hpp include.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Cyd
2026-08-03 14:08:37 -05:00
co-authored by Claude Fable 5
parent 4ace2571d9
commit e40abc86ea
3 changed files with 133 additions and 0 deletions
+117
View File
@@ -56,6 +56,9 @@
#if !defined(BOXSOLID_HPP)
# include <boxsolid.hpp> // BoxedSolid extents -- the ground-snap probe
#endif
#if !defined(RANDOM_HPP)
# include <random.hpp> // Random -- the collision-rattle draw
#endif
#include <dmgtable.hpp> // DamageLookupTable -- the cylinder hit table
#include <player.hpp> // Player::VehicleDeadMessage -- the death notification
#include <btplayer.hpp> // BTPlayer::ScoreMessage -- the kill credit
@@ -778,6 +781,106 @@ Mech::~Mech()
// Entity handler which routes damageZones[zone]->TakeDamage.
//#############################################################################
//
//
//#############################################################################
// DistributeCollisionDamage (binary @0049ffcc, reached ONLY from the damage
// hub's type-0 divert) -- the collision-damage economy.
//
// Collision damage is the manual's "collision damage" TECHNICIAN SETTING:
// gated on the owning player's advanced-damage flag, priced against a
// 100 km/h reference impact, and applied as 0.5-point RATTLE CRITS to random
// internal subsystems. Armor is never touched by a collision.
//
// scale = (2000 / mass) / (100 km/h)^2 / (1 - elasticity^2)
// amount = raw * scale; under 0.5 the tap is FREE
// n = Round(amount * 2) sub-hits of amount/n each
//
// Each sub-hit lands on ONE roster subsystem drawn by cumulative
// collisionCriticalHitWeight against a [0,1) roll, restricted to the
// HeatSink / Gyroscope / Torso families. An un-won roll lands nowhere --
// faithful: the binary does not normalize the weights.
//#############################################################################
//
void
Mech::DistributeCollisionDamage(Damage *damage)
{
Check(this);
Check_Pointer(damage);
BTPlayer
*player = (BTPlayer *)GetPlayerLink();
if (player == NULL || !player->IsAdvancedDamageOn())
{
return;
}
Scalar
reference = 100.0f * 0.27777779f; // 100 km/h in world u/s
Scalar
denominator =
1.0f - elasticityCoefficient * elasticityCoefficient;
Scalar
scale = ((2000.0f / moverMass) / (reference * reference)) / denominator;
damage->damageAmount *= scale;
if (damage->damageAmount < 0.5f)
{
return;
}
int
sub_hits = (int)(damage->damageAmount * 2.0f + 0.5f);
if (sub_hits < 1)
{
sub_hits = 1;
}
damage->damageAmount /= (Scalar)sub_hits;
if (getenv("BT_MECH_LOG"))
{
DEBUG_STREAM << "[colldmg] rattle "
<< (damage->damageAmount * sub_hits) << " pts in "
<< sub_hits << " sub-hits (scale=" << scale << ")"
<< endl << flush;
}
int
hit,
slot;
for (hit = 0; hit < sub_hits; ++hit)
{
Scalar
threshold = (Scalar)Random,
accumulated = 0.0f;
for (slot = 0; slot < GetSubsystemCount(); ++slot)
{
Subsystem
*subsystem = GetSubsystem(slot);
if (subsystem == NULL)
{
continue;
}
if (
!subsystem->IsDerivedFrom(HeatSink::ClassDerivations) &&
!subsystem->IsDerivedFrom(Gyroscope::ClassDerivations) &&
!subsystem->IsDerivedFrom(Torso::ClassDerivations)
)
{
continue;
}
accumulated +=
((MechSubsystem *)subsystem)->CollisionCritWeight();
if (accumulated < threshold)
{
continue;
}
((MechSubsystem *)subsystem)->ApplyDamageAndMeasure(*damage);
break;
}
}
Check_Fpu();
}
void
Mech::TakeDamageMessageHandler(TakeDamageMessage *message)
{
@@ -795,6 +898,20 @@ void
lastInflictingID = message->inflictingEntity;
lastInflictingDamage = message->damageData.damageAmount;
//
// THE COLLISION DIVERT (binary @0x4a0368 -> @0049ffcc): type-0 Collision
// damage NEVER reaches the armor zones. It is priced against a 100 km/h
// reference impact and applied as internal RATTLE crits -- which is the
// answer to the raw kinetic numbers being thousands of points where a
// PPC is ~12. Without this divert a hard wall crash one-shots a vital
// zone through the cylinder lottery (observed live, 5.3.99).
//
if (message->damageData.damageType == Damage::CollisionDamageType)
{
DistributeCollisionDamage(&message->damageData);
return;
}
//
// The cylinder resolve (binary @0x4a0264 tail): an UNAIMED hit arrives
// with invalidDamageZone set -- map its world impact point onto a hull
+8
View File
@@ -378,6 +378,14 @@
unsigned callback_arg,
Scalar carryover,
int move_joints);
//
// The collision-damage economy (binary @0049ffcc): type-0 damage is
// diverted here by the damage hub and rattles internal subsystems --
// armor is never touched by a collision.
//
void
DistributeCollisionDamage(Damage *damage);
static Scalar
BodyClipFinished(
Mech *mech,
+8
View File
@@ -175,6 +175,14 @@
Scalar
ApplyDamageAndMeasure(Damage &damage);
//
// The collision-rattle draw weight (binary subsystem+0x10C) -- read
// by Mech::DistributeCollisionDamage's cumulative roll.
//
Scalar
CollisionCritWeight() const
{ Check(this); return collisionCriticalHitWeight; }
Logical
IsVitalSubsystem() const { Check(this); return vitalSubsystem; }