Gyro: damage fan-out FUN_004b2980 -- the cockpit hit-BOUNCE is live (task #56)
Re-disassembled the unexported gap 0x4b2980-0x4b2d8b byte-exact (capstone over
section_dump.txt) and reconstructed Gyroscope::ApplyDamageResponse:
- zero-damage + Collision(type 0) no-op; hit direction = Damage::damageForce
or a RANDOM horizontal fallback when ~zero (per-component sign roll then
value roll -- binary-legal until senders fill damageForce)
- direction rotated by the yaw-only torso-twist frame (placeRot=(0,twist,0)
-> euler->matrix, the FUN_00408744 raw row-dot reproduced verbatim)
- per-type scaling amount / damageMultiplier[type] * damageResponse[type]
.{trans,pitchRoll,yaw,vibration}; Explosive alone multiplies burstCount;
each clamped at 1.3f
- four kicks: impulse(trans, dir), torque(pitchRoll, dir), impulse(vibration,
vibrationDirection@0x390) and verticalImpulse(yaw, dir)
Layout corrections (byte-verified): gyro+0x390 is ONE Vector3D vibration axis
init (0,1,0) -- was mis-split as scalars animationOffset/Scale/Phase; mech
+0x5c4 is a FLOAT rumble-period countdown (gyroRumbleTimer) -- was mis-typed
int clipLoadGuard.
Call sites wired (binary-gated):
- take-damage hub: mech.cpp TakeDamageMessageHandler, FIRST action (@0x4a0264
in hub FUN_004a0230) -- an invalid-zone hit still bounces
- crushable-icon crunch: mech4.cpp, torque 0.4 along the bounce delta-v + up
impulse 0.2 (@4aa81e/@4aa86c)
- firing recoil: projweap.cpp FireWeapon, damage>3 -> impulse (0,0.6,-1.5)
x damage/16 (@4bc136-4bc19c) via the new Mech::GetGyroSubsystem accessor
DEFERRED (recorded in KB with the byte recipe): the alternate-gait engage
jolt + 0.4s rumble (@4aa158-4aa365) -- its gates are [T3]-flagged and it
mutates the #49/#50-stabilized gait machine.
Verified live: [gyro-dmg] fires per hit with correct type scaling; eyePosition
shows the damped bounce (ramp to ~0.015u, oscillating Y, decay to zero); kill
chain un-regressed; no NaN.
Also recorded in KB: the OFFSET-COCKPIT verification -- Thor's authored
jointeye tranx=+1.13 matches its canopy X-center exactly AND our in-game eye
reproduces it to the hundredth (the placement model is correct per-mech); and
the playable roster is 8 models (strider/raptor/firstrtr/blkjack have assets
but no model resource -- BT_FORCE_MODEL with those names = NULL-resource AV,
cdb-verified, not a code bug).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
cb85517ede
commit
946323b696
+154
-3
@@ -177,6 +177,7 @@ struct GyroLayoutCheck
|
||||
static_assert(offsetof(Gyroscope, bodyWork) == 0x320, "bodyWork @0x320");
|
||||
static_assert(offsetof(Gyroscope, damageMultiplier) == 0x32C, "damageMultiplier @0x32C");
|
||||
static_assert(offsetof(Gyroscope, damageResponse) == 0x340, "damageResponse @0x340");
|
||||
static_assert(offsetof(Gyroscope, vibrationDirection) == 0x390, "vibrationDirection @0x390");
|
||||
static_assert(offsetof(Gyroscope, swayBias) == 0x3A8, "swayBias @0x3A8");
|
||||
static_assert(offsetof(Gyroscope, swayAngle) == 0x3BC, "swayAngle @0x3BC");
|
||||
static_assert(offsetof(Gyroscope, eyeJointNode) == 0x3C8, "eyeJointNode @0x3C8");
|
||||
@@ -306,9 +307,7 @@ Gyroscope::Gyroscope(
|
||||
externalPitchPtr = &spare0; // @0x258 self-pointer (:2933);
|
||||
// bt_mech tail re-points to &torso pitch
|
||||
}
|
||||
animationOffset = 0.0f; // @0x390
|
||||
animationScale = 1.0f; // @0x394
|
||||
animationPhase = 0.0f; // @0x398
|
||||
vibrationDirection = Vector3D(0.0f, 1.0f, 0.0f); // @0x390 the up vibration axis (:2921-2923)
|
||||
swayAngle = 0.0f; // @0x3BC
|
||||
swayVelocity = 0.0f; // @0x3C0
|
||||
swayActive = 0; // @0x3C4
|
||||
@@ -632,6 +631,18 @@ void
|
||||
DEBUG_STREAM << "[gyro] *** NaN DETECTED in integrator state ***\n" << std::flush;
|
||||
}
|
||||
++s_gc;
|
||||
// bounce visibility probe: log the first N frames where the eye is actually
|
||||
// displaced (the periodic sampler above misses short oscillations).
|
||||
static int s_bounceSeen = 0;
|
||||
if (s_glog && s_bounceSeen < 40
|
||||
&& (eyePosition.x > 1e-3f || eyePosition.x < -1e-3f
|
||||
|| eyePosition.y > 1e-3f || eyePosition.y < -1e-3f
|
||||
|| eyePosition.z > 1e-3f || eyePosition.z < -1e-3f))
|
||||
{
|
||||
++s_bounceSeen;
|
||||
DEBUG_STREAM << "[gyro-bounce] eyePos=(" << (float)eyePosition.x << ","
|
||||
<< (float)eyePosition.y << "," << (float)eyePosition.z << ")\n" << std::flush;
|
||||
}
|
||||
if (!NodeRotationEquals(mechJointNode, eyePosition, QuantiseEps)) // FUN_004084fc vs value+0
|
||||
{
|
||||
SetNodeRotation(mechJointNode, eyePosition); // FUN_0041d11c = SetTRANSLATION
|
||||
@@ -728,6 +739,124 @@ void
|
||||
bodyForce.y = bodyForce.x; // *(this+0x30C) = *(this+0x308)
|
||||
}
|
||||
|
||||
//
|
||||
// @004b2980 [T1 -- re-disassembled byte-exact, task #56] -- the damage->gyro
|
||||
// fan-out (the cockpit hit-BOUNCE). Constants from the dump: eps 1e-4
|
||||
// (@0x38d1b717), sign threshold 0.5f (@0x4b2d84), clamp 1.3f (@0x4b2d88).
|
||||
// Collision (type 0) and zero damage no-op. Direction: the Damage record's
|
||||
// damageForce, or a RANDOM horizontal direction when ~zero (per component a
|
||||
// sign roll then a value roll); rotated by the yaw-only torso-twist frame
|
||||
// (placeRot=(0, *externalPitchPtr, 0) -> matrix @workMatrix, the engine
|
||||
// euler->matrix conversion == the binary's euler->quat->matrix FUN_00409a00 +
|
||||
// FUN_0040ab44) then re-normalized. Per-type scaling: amount / multiplier *
|
||||
// response{trans,pitchRoll,yaw,vibration}; the Explosive case alone multiplies
|
||||
// by burstCount. Four kicks: directional impulse (trans), torque (pitchRoll),
|
||||
// a vibration shake along the FIXED member axis vibrationDirection@0x390 (up),
|
||||
// and the vertical impulse fed by the row's third field ("yaw").
|
||||
//
|
||||
void
|
||||
Gyroscope::ApplyDamageResponse(const Damage &damage)
|
||||
{
|
||||
if (damage.damageAmount == 0.0f) // @4b298c (0.0f @0x4b2d80)
|
||||
return;
|
||||
if (damage.damageType == Damage::CollisionDamageType) // @4b299e -- collisions never bounce here
|
||||
return;
|
||||
|
||||
Scalar trans = 0.0f; // ebp-0x10
|
||||
Scalar pitchRoll = 0.0f; // ebp-0x0C
|
||||
Scalar yaw = 0.0f; // ebp-0x08
|
||||
Scalar vibration = 0.0f; // ebp-0x04
|
||||
|
||||
Vector3D dir; // ebp-0x1C
|
||||
if (Close_Enough(damage.damageForce, Vector3D(0.0f, 0.0f, 0.0f), 1e-4f)) // @4b29bd FUN_004084fc
|
||||
{
|
||||
// random horizontal: sign roll (>= 0.5 keeps +) then value roll, y=0
|
||||
dir.x = (RandomUnit() >= 0.5f) ? RandomUnit() : -RandomUnit(); // @4b29d7
|
||||
dir.z = (RandomUnit() >= 0.5f) ? RandomUnit() : -RandomUnit(); // @4b2a0d
|
||||
dir.y = 0.0f; // @4b2a43
|
||||
}
|
||||
else
|
||||
{
|
||||
dir = damage.damageForce; // @4b2a4a FUN_00408440
|
||||
}
|
||||
dir.Normalize(dir); // @4b2a62 FUN_004087f4 (unguarded in the binary too)
|
||||
|
||||
// @4b2a67-4b2af5: yaw-only body frame from the torso twist; rotate the
|
||||
// WORLD hit direction into it (FUN_00408744 raw row-dot on entries[0..2]/
|
||||
// [4..6]/[8..10] -- reproduced verbatim below) and re-normalize. The
|
||||
// placement state @0x28C/0x298/0x2A8 is written for member fidelity.
|
||||
placeRot = Vector3D(0.0f, *externalPitchPtr, 0.0f); // gyro+0x2A8..0x2B0
|
||||
placePos = Vector3D(0.0f, 0.0f, 0.0f); // @4b2aa8
|
||||
{
|
||||
AffineMatrix m;
|
||||
m = EulerAngles(Radian(placeRot.x), Radian(placeRot.y), Radian(placeRot.z));
|
||||
for (int i = 0; i < 12; ++i) workMatrix[i] = m.entries[i]; // @4b2ac2 FUN_0040ab44
|
||||
Vector3D tmp = dir; // ebp-0x28 @4b2ac7
|
||||
dir.x = tmp.x*workMatrix[0] + tmp.y*workMatrix[1] + tmp.z*workMatrix[2]; // FUN_00408744
|
||||
dir.y = tmp.x*workMatrix[4] + tmp.y*workMatrix[5] + tmp.z*workMatrix[6];
|
||||
dir.z = tmp.x*workMatrix[8] + tmp.y*workMatrix[9] + tmp.z*workMatrix[10];
|
||||
}
|
||||
dir.Normalize(dir); // @4b2aed
|
||||
|
||||
// @4b2afd: per-type scaling (jump table @0x4b2b10; type > 4 leaves all
|
||||
// four at zero). Each term computed independently, matching the binary's
|
||||
// per-term fld/fdiv/fmul.
|
||||
switch (damage.damageType)
|
||||
{
|
||||
case Damage::BallisticDamageType: // @4b2b3d mult@0x330 resp@0x350
|
||||
trans = damage.damageAmount / damageMultiplier[1] * damageResponse[1].trans;
|
||||
pitchRoll = damage.damageAmount / damageMultiplier[1] * damageResponse[1].pitchRoll;
|
||||
yaw = damage.damageAmount / damageMultiplier[1] * damageResponse[1].yaw;
|
||||
vibration = damage.damageAmount / damageMultiplier[1] * damageResponse[1].vibration;
|
||||
break;
|
||||
case Damage::ExplosiveDamageType: // @4b2b8a the ONLY case reading burstCount
|
||||
trans = (Scalar)damage.burstCount * damage.damageAmount / damageMultiplier[2] * damageResponse[2].trans;
|
||||
pitchRoll = (Scalar)damage.burstCount * damage.damageAmount / damageMultiplier[2] * damageResponse[2].pitchRoll;
|
||||
yaw = (Scalar)damage.burstCount * damage.damageAmount / damageMultiplier[2] * damageResponse[2].yaw;
|
||||
vibration = (Scalar)damage.burstCount * damage.damageAmount / damageMultiplier[2] * damageResponse[2].vibration;
|
||||
break;
|
||||
case Damage::LaserDamageType: // @4b2be3 mult@0x338 resp@0x370
|
||||
trans = damage.damageAmount / damageMultiplier[3] * damageResponse[3].trans;
|
||||
pitchRoll = damage.damageAmount / damageMultiplier[3] * damageResponse[3].pitchRoll;
|
||||
yaw = damage.damageAmount / damageMultiplier[3] * damageResponse[3].yaw;
|
||||
vibration = damage.damageAmount / damageMultiplier[3] * damageResponse[3].vibration;
|
||||
break;
|
||||
case Damage::EnergyDamageType: // @4b2c2d mult@0x33C resp@0x380
|
||||
trans = damage.damageAmount / damageMultiplier[4] * damageResponse[4].trans;
|
||||
pitchRoll = damage.damageAmount / damageMultiplier[4] * damageResponse[4].pitchRoll;
|
||||
yaw = damage.damageAmount / damageMultiplier[4] * damageResponse[4].yaw;
|
||||
vibration = damage.damageAmount / damageMultiplier[4] * damageResponse[4].vibration;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// @4b2c75-4b2ce2: upper-clamp each at 1.3f; NO lower clamp.
|
||||
if (trans > 1.3f) trans = 1.3f;
|
||||
if (pitchRoll > 1.3f) pitchRoll = 1.3f;
|
||||
if (yaw > 1.3f) yaw = 1.3f;
|
||||
if (vibration > 1.3f) vibration = 1.3f;
|
||||
|
||||
if (getenv("BT_GYRO_LOG"))
|
||||
DEBUG_STREAM << "[gyro-dmg] type=" << (int)damage.damageType
|
||||
<< " amt=" << (float)damage.damageAmount << " burst=" << (int)damage.burstCount
|
||||
<< " dir=(" << (float)dir.x << "," << (float)dir.y << "," << (float)dir.z
|
||||
<< ") t/p/y/v=" << (float)trans << "/" << (float)pitchRoll << "/"
|
||||
<< (float)yaw << "/" << (float)vibration << "\n" << std::flush;
|
||||
|
||||
ApplyDamageImpulse (dir.x, dir.y, dir.z, trans); // @4b2d00 the directional knock
|
||||
ApplyDamageTorque (dir.x, dir.y, dir.z, pitchRoll); // @4b2d23
|
||||
ApplyDamageImpulse (vibrationDirection.x, vibrationDirection.y,
|
||||
vibrationDirection.z, vibration); // @4b2d4b the up-shake
|
||||
ApplyVerticalImpulse(dir.x, dir.y, dir.z, yaw); // @4b2d6e
|
||||
|
||||
if (getenv("BT_GYRO_LOG"))
|
||||
DEBUG_STREAM << "[gyro-dmg] post-kick exag=" << (float)exageration
|
||||
<< " eyeForce=(" << (float)eyeForce.x << "," << (float)eyeForce.y << "," << (float)eyeForce.z
|
||||
<< ") bodyForce=(" << (float)bodyForce.x << "," << (float)bodyForce.y << ","
|
||||
<< (float)bodyForce.z << ")\n" << std::flush;
|
||||
}
|
||||
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// CreateStreamedSubsystem -- Gyroscope
|
||||
@@ -905,3 +1034,25 @@ void GyroBindExternalPitch(Subsystem *gyro, Scalar *pitch)
|
||||
if (gyro != 0)
|
||||
static_cast<Gyroscope *>(gyro)->BindExternalPitch(pitch);
|
||||
}
|
||||
|
||||
//===========================================================================//
|
||||
// Damage->gyro bridges (task #56 fan-out; binary call sites: the take-damage
|
||||
// hub @0x4a02fb, the performance crunch/jolt/rumble kicks @0x4aa254/0x4aa288/
|
||||
// 0x4aa342/0x4aa81e/0x4aa86c, and the firing-recoil kick @0x4bc194).
|
||||
// Complete-type-TU pattern: callers hold only Subsystem*.
|
||||
//===========================================================================//
|
||||
void GyroApplyDamage(Subsystem *gyro, const Damage &damage)
|
||||
{
|
||||
if (gyro != 0)
|
||||
static_cast<Gyroscope *>(gyro)->ApplyDamageResponse(damage);
|
||||
}
|
||||
void GyroApplyDamageImpulse(Subsystem *gyro, Scalar x, Scalar y, Scalar z, Scalar magnitude)
|
||||
{
|
||||
if (gyro != 0)
|
||||
static_cast<Gyroscope *>(gyro)->ApplyDamageImpulse(x, y, z, magnitude);
|
||||
}
|
||||
void GyroApplyDamageTorque(Subsystem *gyro, Scalar x, Scalar y, Scalar z, Scalar magnitude)
|
||||
{
|
||||
if (gyro != 0)
|
||||
static_cast<Gyroscope *>(gyro)->ApplyDamageTorque(x, y, z, magnitude);
|
||||
}
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
//##################### Forward Class Declarations #######################
|
||||
class Mech;
|
||||
class Joint; // engine skeleton node (JOINT.h); eye/mech joint targets
|
||||
class Damage; // engine damage record (DAMAGE.h); ApplyDamageResponse input
|
||||
|
||||
//###########################################################################
|
||||
//################# Gyroscope Model Resource ##########################
|
||||
@@ -212,6 +213,13 @@ class Joint; // engine skeleton node (JOINT.h); eye/mech joint targets
|
||||
Scalar x, Scalar y, Scalar z, Scalar magnitude);
|
||||
void ApplyVerticalImpulse( // @004b2e50
|
||||
Scalar pitch, Scalar /*y*/, Scalar /*z*/, Scalar magnitude);
|
||||
// @004b2980 -- the damage->gyro fan-out (task #56): normalize the hit
|
||||
// direction (random horizontal if ~zero), rotate into the torso-twist
|
||||
// frame, scale by the per-damage-type multiplier/response curves, clamp
|
||||
// at 1.3, fire the four Apply* kicks. The binary passes the whole
|
||||
// Damage BY VALUE (12 dwords, caller add esp,0x34) but never mutates it
|
||||
// and never reads surfaceNormal/impactPoint -- const-ref is byte-equal.
|
||||
void ApplyDamageResponse(const Damage &damage);
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Per-frame joint-write dispatch (called from the MECH master performance
|
||||
@@ -320,9 +328,12 @@ class Joint; // engine skeleton node (JOINT.h); eye/mech joint targets
|
||||
Gyroscope__DamageResponse
|
||||
damageResponse[5]; // @0x340 res +0x1CC..0x21B (5 x {trans,pitchRoll,yaw,vibration})
|
||||
|
||||
Scalar animationOffset; // @0x390 (this[0xE4]) idle-noise phase, init 0
|
||||
Scalar animationScale; // @0x394 (this[0xE5]) init 1.0f
|
||||
Scalar animationPhase; // @0x398 (this[0xE6]) init 0
|
||||
// @0x390 (this[0xE4..0xE6]) -- the VIBRATION AXIS: ctor init (0,1,0) =
|
||||
// straight up. Read as ONE Vector3D by ApplyDamageResponse @4b2d2b (the
|
||||
// second ApplyDamageImpulse = the vibration shake). [T1 re-disassembled]
|
||||
// (Was mis-split as scalars animationOffset/animationScale/animationPhase
|
||||
// -- the 0/1/0 "init pattern" was this unit vector all along.)
|
||||
Vector3D vibrationDirection; // @0x390 init (0.0f, 1.0f, 0.0f)
|
||||
|
||||
Scalar maxAnimationNoise; // @0x39C resource +0xFC
|
||||
Scalar minAnimationNoise; // @0x3A0 resource +0x100
|
||||
|
||||
@@ -216,6 +216,7 @@ extern Subsystem *CreateGaussRifleSubsystem(Mech *, int, void *); // 0xBCE (WAVE
|
||||
extern Subsystem *CreateGyroSubsystem(Mech *, int, void *); // 0xBC4 (task #56 -- ctor/integrators byte-exact, ENABLED)
|
||||
extern void GyroFrameJointWrite(Subsystem *, Scalar, int, int); // task #56 -- mech-performance joint-write dispatch (mech4.cpp)
|
||||
extern void GyroBindExternalPitch(Subsystem *, Scalar *); // task #56 -- post-stream gyro+0x258 = &torso twist
|
||||
extern void GyroApplyDamage(Subsystem *, const Damage &); // task #56 -- the @0x4a0264 hit-bounce fan-out
|
||||
extern Scalar *BTGetTorsoTwistAddr(Subsystem *); // torso.cpp bridge (complete-type TU)
|
||||
|
||||
// Post-stream entity glue + cosmetic objects.
|
||||
@@ -594,6 +595,16 @@ void
|
||||
<< " table=" << (void*)damageLookupTable
|
||||
<< " zones=" << damageZoneCount << "\n" << std::flush;
|
||||
|
||||
// Binary @0x4a0264-0x4a0300 (hub FUN_004a0230): the cockpit hit-BOUNCE.
|
||||
// Feed the gyro the raw Damage record FIRST -- before inflictor
|
||||
// bookkeeping, threat feed and zone resolution (an invalid-zone hit still
|
||||
// bounces). Sole gate: non-null gyro (mech+0x528). The fan-out no-ops
|
||||
// CollisionDamageType(0) and damageAmount==0 itself (@4b298c/@4b299e); on
|
||||
// a replicant the binary only WARNS ("Replicant Mech recieving
|
||||
// takedamagemessage!", MECH.CPP:986) and proceeds -- no replicant gate.
|
||||
if (gyroSubsystem != 0)
|
||||
GyroApplyDamage(gyroSubsystem, message->damageData);
|
||||
|
||||
// Maintain the last-attacker bookkeeping (mech[0x43c]): read by the
|
||||
// DamageZone LOD router (same-attacker redirect reuse, mechdmg.cpp:374)
|
||||
// and by the damage-band effect orientation. Was declared but never
|
||||
|
||||
@@ -423,6 +423,9 @@ struct ShotDescriptor
|
||||
public:
|
||||
// Cache accessors for the controls mapper (@004afd10 reads mech+0x438/+0x5b4).
|
||||
Subsystem *GetTorsoSubsystem() { return sinkSourceSubsystem; }
|
||||
// Gyro accessor (task #56): the firing-recoil kick (projweap.cpp,
|
||||
// binary @4bc194) reads mech+0x528.
|
||||
Subsystem *GetGyroSubsystem() { return gyroSubsystem; }
|
||||
Subsystem *GetHudSubsystem() { return hudSubsystem; }
|
||||
// Reachable horizontal firing half-arc (radians) the mech's torso can bring
|
||||
// its guns to bear -- the wider torso twist limit, or 0 for a fixed torso.
|
||||
@@ -659,7 +662,9 @@ protected:
|
||||
// speedDemand = topSpeed@0x34c * throttle * THIS). No writer found in the
|
||||
// decomp windows (likely model/status-driven); ctor inits 1.0 (neutral).
|
||||
Scalar forwardThrottleScale; // @0x5c0
|
||||
int clipLoadGuard; // @0x5c4
|
||||
Scalar gyroRumbleTimer; // @0x5c4 binary: FLOAT rumble-period countdown
|
||||
// (engaged-gait rumble @4aa2eb-4aa35f, task #56);
|
||||
// reset at clip load. (Was mis-typed int clipLoadGuard.)
|
||||
Scalar globalTimeScale; // @0x5a8
|
||||
Scalar idleStrideScale; // @0x5ac
|
||||
Scalar gimpCycleRate; // @0x5b0
|
||||
|
||||
@@ -221,7 +221,7 @@ template<class...A> inline void *FUN_00406db4(A&&...) { return 0; } // ResourceF
|
||||
// @0x588 hasCrashSet (int)? set 1 iff the third optional clip set exists
|
||||
// @0x5b8 groundCycleRate forward-cycle slew rate, on-ground (-> 0x344)
|
||||
// @0x5bc airborneCycleRate forward-cycle slew rate, airborne (-> 0x344)
|
||||
// @0x5c4 clipLoadGuard (int) reset to 0 at the top of a clip load
|
||||
// @0x5c4 gyroRumbleTimer (float) reset to 0 at the top of a clip load
|
||||
// @0x5cc animationClips[] (ptr[]) gait-state -> clip handle (mech2); indexed
|
||||
// by MeasureClipStride
|
||||
// @0x5d4 crashClipB (ptr) optional-set clip handle
|
||||
@@ -327,7 +327,7 @@ void
|
||||
{
|
||||
const char *prefix = model->animationPrefix; // param_2 + 0x40
|
||||
|
||||
clipLoadGuard = 0; // this+0x5c4
|
||||
gyroRumbleTimer = 0.0f; // this+0x5c4 (float; bit-identical to the old int 0)
|
||||
|
||||
// --- stand -> walk : standSpeed is that clip's final-keyframe stride ----
|
||||
namedClip[ 0] = *ResolveAnimationClip(prefix, "swr"); // 0x5e0
|
||||
|
||||
@@ -3307,7 +3307,20 @@ void
|
||||
localOrigin.linearPosition = savedPos;
|
||||
MoveCollisionVolume();
|
||||
dmg.damageAmount = 0.0f;
|
||||
// gyro crunch feed (0.4*normal + 0.2*up): DORMANT until the Gyroscope un-stub
|
||||
// Binary @4aa7ce-4aa871 (task #56): the gyro CRUNCH. n = Normalize(
|
||||
// dmg.damageForce) (the delta-v across the bounce, filled by the engine
|
||||
// Mover::ProcessCollisionList -- no new plumbing), then a torque along
|
||||
// n @ 0.4 (@4aa81e, 0x3ecccccd) + an upward impulse @ 0.2 (@4aa86c,
|
||||
// 0x3e4ccccd). Normalize is unguarded in the binary too.
|
||||
if (gyroSubsystem != 0)
|
||||
{
|
||||
extern void GyroApplyDamageTorque (Subsystem *, Scalar, Scalar, Scalar, Scalar);
|
||||
extern void GyroApplyDamageImpulse(Subsystem *, Scalar, Scalar, Scalar, Scalar);
|
||||
Vector3D n;
|
||||
n.Normalize(dmg.damageForce);
|
||||
GyroApplyDamageTorque (gyroSubsystem, n.x, n.y, n.z, 0.4f); // @4aa81e
|
||||
GyroApplyDamageImpulse(gyroSubsystem, 0.0f, 1.0f, 0.0f, 0.2f); // @4aa86c
|
||||
}
|
||||
if (GroundLog())
|
||||
DEBUG_STREAM << "[ground] CRUNCH (crushable icon) at ("
|
||||
<< savedPos.x << ", " << savedPos.z << ")\n" << std::flush;
|
||||
|
||||
@@ -604,6 +604,20 @@ void
|
||||
{
|
||||
Check(this);
|
||||
|
||||
// Binary @4bc136-4bc19c (task #56): the firing RECOIL kick, BEFORE the ammo
|
||||
// pull (no early-out precedes it in the binary). Gate: per-shot damage >
|
||||
// 3.0f (@0x4bc3f4) && the owner mech has a gyro. Direction (0, 0.6, -1.5)
|
||||
// (0x3f19999a/0xbfc00000), magnitude = damageAmount / 16 (@0x4bc3f8).
|
||||
// (The binary's preceding heat add is a separate pre-existing gap.)
|
||||
if (damageData.damageAmount > 3.0f && owner != 0)
|
||||
{
|
||||
extern void GyroApplyDamageImpulse(Subsystem *, Scalar, Scalar, Scalar, Scalar);
|
||||
Subsystem *g = ((Mech *)owner)->GetGyroSubsystem(); // mech+0x528
|
||||
if (g != 0)
|
||||
GyroApplyDamageImpulse(g, 0.0f, 0.6f, -1.5f,
|
||||
damageData.damageAmount * 0.0625f);
|
||||
}
|
||||
|
||||
// Pull a round; a dry / not-ready bin latches NoAmmo and aborts the shot.
|
||||
if (!ConsumeRound()) // FUN_004bd4f4 (AmmoBin::FeedAmmo)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user