BT410 5.3.102: ProcessCollision -- trees fall, walls do not, and both sides of a ram hurt
The per-contact responder (@004abb40), overriding the engine's protected
virtual per the MECH4.NOTES.md decode:
THE SEPARATING GATE: a contact whose relative velocity points away from
the surface ((normal . rel) < -1e-4) is skipped entirely -- no damage
while pulling apart.
MECH-VS-MECH: another mech takes the collision damage too, zone -1, its
own cylinder resolving where the ram landed. Both sides of a ram hurt,
and both route through the 5.3.100 economy, so a ram rattles rather than
executes.
THE CRUSHABLE SENTINEL: a CulturalIcon takes its crunch dispatch, and one
WITHOUT the StoppingCollisionVolume flag then sets the amount to exactly
0.00123f -- Simulate's response block reads that value as "the move
stands", restores the post-snap saves, and drives on through. Trees
fall; walls do not.
THE CONTACT ACCUMULATOR feeds the audio-bound collisionState indicator
(0->1 fires the impact sound once per contact, not per frame) and
captures the impact speed for the authored volume/brightness scales.
[T3 simplification, tagged in source: the authentic InitialHit/Slide
split keys StaticBounce's OUT coefficients against an unidentified
literal @0x4ac048.]
LIVE, one arena run, all three behaviours in the wild and zero deaths:
[crash] CRUNCH (crushable) at (371.551,-102.862) <- drove through it
[colldmg] rattle 1.72691 pts in 3 sub-hits
[crash] BLOCK dmg=47969.6 iv2=932.659 KNOCKDOWN <- wall still walls
[crash] BLOCK dmg=630 iv2=12.5 (x5, free taps) <- grind still free
STAGED remainder, named in MECH4.NOTES.md: the gyro crunch feed (torque 0.4
/ impulse 0.2 -- the gyro is a feel-wave stub), and the authentic
InitialHit/Slide split pending the @0x4ac048 literal.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -59,6 +59,9 @@
|
||||
#if !defined(RANDOM_HPP)
|
||||
# include <random.hpp> // Random -- the collision-rattle draw
|
||||
#endif
|
||||
#if !defined(CULTURAL_HPP)
|
||||
# include <cultural.hpp> // CulturalIcon -- the crunch / crushable sentinel
|
||||
#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
|
||||
@@ -256,6 +259,7 @@ Mech::Mech(
|
||||
legResetLatch = 0;
|
||||
bodyResetLatch = 0;
|
||||
limpModeOverride = 0;
|
||||
collisionTemporaryState = 0;
|
||||
{
|
||||
const char *fl = getenv("BT_FORCE_LIMP");
|
||||
if (fl != NULL && (*fl == '3' || *fl == '4'))
|
||||
@@ -781,6 +785,166 @@ Mech::~Mech()
|
||||
// Entity handler which routes damageZones[zone]->TakeDamage.
|
||||
//#############################################################################
|
||||
//
|
||||
//
|
||||
//#############################################################################
|
||||
// ProcessCollision (binary @004abb40) -- the per-contact collision
|
||||
// responder, overriding the engine's protected virtual. The base head is
|
||||
// Mover::ProcessCollision verbatim (MOVER.CPP:1354); what the Mech adds is
|
||||
// WHO it hit:
|
||||
//
|
||||
// * A separating contact -- one whose relative velocity points away from
|
||||
// the surface -- is skipped entirely (the -1e-4 gate). No damage while
|
||||
// pulling apart.
|
||||
//
|
||||
// * Another MECH takes the collision damage too (zone -1: the victim's
|
||||
// own cylinder resolves it). Both sides of a ram hurt.
|
||||
//
|
||||
// * A CulturalIcon takes the crunch, and a CRUSHABLE one (no
|
||||
// StoppingCollisionVolume flag) then sets the amount to the 0.00123f
|
||||
// WALK-THROUGH SENTINEL -- Simulate's response block reads exactly that
|
||||
// value as "the move stands": the mech drives THROUGH the prop while
|
||||
// the prop takes the damage. Trees fall, walls do not.
|
||||
//#############################################################################
|
||||
//
|
||||
void
|
||||
Mech::ProcessCollision(
|
||||
Scalar time_slice,
|
||||
BoxedSolidCollision &collision,
|
||||
const Point3D &old_position,
|
||||
Damage *damage
|
||||
)
|
||||
{
|
||||
Check(this);
|
||||
Verify(time_slice > 0.0f);
|
||||
Check(&collision);
|
||||
Check_Pointer(damage);
|
||||
|
||||
Scalar
|
||||
penetration;
|
||||
|
||||
if (
|
||||
!collisionVolume->ProcessCollision(
|
||||
collision,
|
||||
worldLinearVelocity,
|
||||
lastCollisionList,
|
||||
&damage->surfaceNormal,
|
||||
&penetration)
|
||||
)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Max_Clamp(penetration, time_slice);
|
||||
Scalar
|
||||
ratio = penetration / time_slice,
|
||||
elasticity = elasticityCoefficient,
|
||||
friction = frictionCoefficient;
|
||||
|
||||
Simulation
|
||||
*owner_simulation = collision.GetTreeVolume()->GetOwningSimulation();
|
||||
|
||||
damage->damageAmount =
|
||||
StaticBounce(
|
||||
old_position,
|
||||
time_slice,
|
||||
ratio,
|
||||
damage->surfaceNormal,
|
||||
&elasticity,
|
||||
minimumBounceSpeed,
|
||||
&friction);
|
||||
|
||||
Entity
|
||||
*other = (Entity *)owner_simulation;
|
||||
|
||||
if (other != NULL && other->IsDerivedFrom(Mover::ClassDerivations))
|
||||
{
|
||||
Vector3D
|
||||
relative;
|
||||
relative.Subtract(
|
||||
worldLinearVelocity,
|
||||
((Mover *)other)->GetWorldLinearVelocity());
|
||||
if (damage->surfaceNormal * relative < -1.0e-4f)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (other->IsDerivedFrom(Mech::ClassDerivations))
|
||||
{
|
||||
Damage
|
||||
ram = *damage;
|
||||
ram.impactPoint = Point3D(
|
||||
(collision.collisionSlice.minX + collision.collisionSlice.maxX) * 0.5f,
|
||||
(collision.collisionSlice.minY + collision.collisionSlice.maxY) * 0.5f,
|
||||
(collision.collisionSlice.minZ + collision.collisionSlice.maxZ) * 0.5f);
|
||||
|
||||
Entity::TakeDamageMessage
|
||||
hit(
|
||||
Entity::TakeDamageMessageID,
|
||||
sizeof(Entity::TakeDamageMessage),
|
||||
GetEntityID(),
|
||||
-1,
|
||||
ram
|
||||
);
|
||||
other->Dispatch(&hit);
|
||||
}
|
||||
}
|
||||
|
||||
if (other != NULL && other->IsDerivedFrom(CulturalIcon::ClassDerivations))
|
||||
{
|
||||
Logical
|
||||
stopping =
|
||||
((CulturalIcon *)other)->IsStoppingCollisionVolume();
|
||||
|
||||
//
|
||||
// The separating gate against a STATIC icon (its velocity is zero,
|
||||
// which the 1995 compiler inlined away).
|
||||
//
|
||||
if (damage->surfaceNormal * worldLinearVelocity < -1.0e-4f)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// The crunch goes out BEFORE the sentinel overwrites the amount.
|
||||
//
|
||||
{
|
||||
Damage
|
||||
crunch = *damage;
|
||||
crunch.impactPoint = Point3D(
|
||||
(collision.collisionSlice.minX + collision.collisionSlice.maxX) * 0.5f,
|
||||
(collision.collisionSlice.minY + collision.collisionSlice.maxY) * 0.5f,
|
||||
(collision.collisionSlice.minZ + collision.collisionSlice.maxZ) * 0.5f);
|
||||
|
||||
Entity::TakeDamageMessage
|
||||
hit(
|
||||
Entity::TakeDamageMessageID,
|
||||
sizeof(Entity::TakeDamageMessage),
|
||||
GetEntityID(),
|
||||
-1,
|
||||
crunch
|
||||
);
|
||||
other->Dispatch(&hit);
|
||||
}
|
||||
|
||||
if (!stopping)
|
||||
{
|
||||
damage->damageAmount = 0.00123f;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// The contact-state accumulator. [T3 simplification, per the decomp's
|
||||
// dominant branch: the authentic InitialHit(1)/Slide(2) split keys the
|
||||
// StaticBounce OUT coefficients against an unidentified literal
|
||||
// (@0x4ac048); a resolved contact IS an impact, so accumulate 1 and
|
||||
// capture the impact speed for the audio scales.]
|
||||
//
|
||||
if (collisionTemporaryState == 0)
|
||||
{
|
||||
collisionTemporaryState = 1;
|
||||
collisionSpeed = worldLinearVelocity.Length();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
//#############################################################################
|
||||
// DistributeCollisionDamage (binary @0049ffcc, reached ONLY from the damage
|
||||
@@ -1741,11 +1905,17 @@ void
|
||||
{
|
||||
Vector3D
|
||||
impact_velocity = worldLinearVelocity;
|
||||
Vector3D
|
||||
post_snap_velocity = worldLinearVelocity;
|
||||
Point3D
|
||||
post_snap_position = localOrigin.linearPosition;
|
||||
BoxedSolidCollisionList
|
||||
*collisions = GetCurrentCollisions();
|
||||
Damage
|
||||
collision_damage;
|
||||
|
||||
collisionTemporaryState = 0;
|
||||
|
||||
if (collisions != NULL)
|
||||
{
|
||||
ProcessCollisionList(
|
||||
@@ -1753,7 +1923,36 @@ void
|
||||
&collision_damage);
|
||||
}
|
||||
|
||||
if (collision_damage.damageAmount > 0.0f)
|
||||
//
|
||||
// 0->1 on a fresh contact fires the impact sound; SetState only
|
||||
// fires on change, so a pressed-against contact plays once.
|
||||
//
|
||||
collisionState.SetState((unsigned)collisionTemporaryState);
|
||||
|
||||
if (collision_damage.damageAmount == 0.00123f)
|
||||
{
|
||||
//
|
||||
// The crushable-icon WALK-THROUGH sentinel (ProcessCollision):
|
||||
// the move STANDS -- restore the post-snap saves and undo the
|
||||
// bounce. The prop took its crunch in the dispatch; the mech
|
||||
// drives on through. (The gyro crunch feed -- torque 0.4 along
|
||||
// the normalized force, upward impulse 0.2, binary
|
||||
// @4aa81e/@4aa86c -- is STAGED with the gyro feel wave.)
|
||||
//
|
||||
worldLinearVelocity = post_snap_velocity;
|
||||
localOrigin.linearPosition = post_snap_position;
|
||||
localToWorld = localOrigin;
|
||||
MoveCollisionVolume();
|
||||
collision_damage.damageAmount = 0.0f;
|
||||
|
||||
if (getenv("BT_MECH_LOG"))
|
||||
{
|
||||
DEBUG_STREAM << "[crash] CRUNCH (crushable) at ("
|
||||
<< post_snap_position.x << ","
|
||||
<< post_snap_position.z << ")" << endl << flush;
|
||||
}
|
||||
}
|
||||
else if (collision_damage.damageAmount > 0.0f)
|
||||
{
|
||||
worldLinearVelocity = Vector3D(0.0f, 0.0f, 0.0f);
|
||||
localOrigin.linearPosition = frame_start_position;
|
||||
|
||||
@@ -386,6 +386,24 @@
|
||||
void
|
||||
DistributeCollisionDamage(Damage *damage);
|
||||
|
||||
//
|
||||
// The per-contact collision responder (binary @004abb40) -- the
|
||||
// override of the engine's protected virtual. Base resolution plus:
|
||||
// the separating-contact gate, the mech-vs-mech ram dispatch, the
|
||||
// CulturalIcon crunch, and the 0.00123f walk-through sentinel for
|
||||
// crushable props. See MECH4.NOTES.md for the decode.
|
||||
//
|
||||
void
|
||||
ProcessCollision(
|
||||
Scalar time_slice,
|
||||
BoxedSolidCollision &collision,
|
||||
const Point3D &old_position,
|
||||
Damage *damage);
|
||||
|
||||
int collisionTemporaryState; // binary mech+0x44c -- the frame's
|
||||
// contact accumulator; pushed into
|
||||
// collisionState once per frame
|
||||
|
||||
static Scalar
|
||||
BodyClipFinished(
|
||||
Mech *mech,
|
||||
|
||||
Reference in New Issue
Block a user