The feel wave's cornerstone, transcribed from the task-#56 byte-exact donor
per the GYRO.NOTES.md plan:
THE SPRINGS. IntegrateEyeJoint (@004b2ec0): the eye TRANSLATION spring
whose equilibrium (posSpring+negSpring)/2 IS the authentic steady eye
offset, with the byte-verified quirks intact -- the damping step
OVERWRITES the force accumulator (it carries last frame's damping and
the damage impulses by design) and the position step has NO dt.
IntegrateBody (@004b30ec): the body ROTATION spring, X/Z-crossed on the
spring force and crossed AGAIN on the orientation step.
THE SIM (@004b275c): chain the power watch (5.3.109's mirrors), slew the
sway angle between the powered and impaired percentages -- impaired reads
the gyro's OWN mirrored alarms: watchdog != Ready or heat == Failure --
band-clamp, run both integrators. The Performance ends there.
THE JOINT WRITES (@004b33e0/@004b34ec) live where the binary calls them:
the MECH master-perf tail after the gait pass, death-gated -- the idle
sway onto the EyeJoint, and TRANSLATION <- eyePosition + ROTATION <-
bodyOrientation onto 'jointeye', the BallTranslation joint the cockpit
eyepoint rides. Every write QuantiseEps-gated.
THE HIT BOUNCE (@004b2980): every non-collision hit shakes the cockpit.
Direction from the damage force (random horizontal when ~zero), rotated
into the yaw-only torso frame, per-type priced amount/multiplier*response
(Explosive alone reads burstCount), clamped 1.3 UPPER only, then the four
kicks -- impulse, torque (which zeroes .y and negates itself), the fixed
up-axis vibration, and the vertical mirror. Fed from the damage hub's
step 1, BEFORE zone resolution -- an invalid-zone hit still shakes you.
THE CRUNCH: driving through a crushable prop now kicks the gyro (torque
0.4 along the contact normal, upward impulse 0.2) -- the 5.3.102 staged
site, closed.
Resource: the five DamageResponse quads {trans,pitchRoll,yaw,vibration}
appended after the multipliers (record 0x21C, binary-verified layout) --
they were always in the stream; we simply never read them. The ctor now
zeroes every accumulator (the donor's history records a NaN poison from
exactly that omission), copies the clamp pairs (body doubled), self-points
externalPitchPtr at spare0 until a torso binds it, and REGISTERS the
Performance on the master -- it ticks live, no fault.
Stub census: 19 across 13 files.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
604 lines
18 KiB
C++
604 lines
18 KiB
C++
//===========================================================================//
|
|
// File: gyro.cpp //
|
|
// Project: BattleTech Brick: Mech subsystems //
|
|
// Contents: Gyroscope -- eye/body stabilisation //
|
|
//---------------------------------------------------------------------------//
|
|
// Copyright (C) 1995, Virtual World Entertainment, Inc. //
|
|
// All Rights reserved worldwide //
|
|
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL //
|
|
//===========================================================================//
|
|
|
|
#include <bt.hpp>
|
|
#pragma hdrstop
|
|
|
|
#if !defined(GYRO_HPP)
|
|
# include <gyro.hpp>
|
|
#endif
|
|
|
|
#if !defined(MECH_HPP)
|
|
# include <mech.hpp>
|
|
#endif
|
|
|
|
#if !defined(RANDOM_HPP)
|
|
# include <random.hpp>
|
|
#endif
|
|
|
|
Derivation
|
|
Gyroscope::ClassDerivations(
|
|
PowerWatcher::ClassDerivations,
|
|
"Gyroscope"
|
|
);
|
|
|
|
Gyroscope::SharedData
|
|
Gyroscope::DefaultData(
|
|
Gyroscope::ClassDerivations,
|
|
Subsystem::MessageHandlers,
|
|
Subsystem::AttributeIndex,
|
|
Subsystem::StateCount
|
|
);
|
|
|
|
Gyroscope::Gyroscope(
|
|
Mech *owner,
|
|
int subsystem_ID,
|
|
SubsystemResource *r,
|
|
SharedData &shared_data
|
|
):
|
|
PowerWatcher(owner, subsystem_ID, r, shared_data)
|
|
{
|
|
Check(owner);
|
|
Check_Pointer(r);
|
|
|
|
exageration = r->exageration;
|
|
maxAnimationNoise = r->maxAnimationNoise;
|
|
minAnimationNoise = r->minAnimationNoise;
|
|
rotationPerSecond = r->rotationPerSecond;
|
|
percentageOnNormal = r->percentageOnNormal;
|
|
percentageOnDestruction = r->percentageOnDestruction;
|
|
percentageOnDegradation = r->percentageOnDegradation;
|
|
percentageOnFailure = r->percentageOnFailure;
|
|
|
|
springConstant = r->springConstant;
|
|
dampingConstant = r->dampingConstant;
|
|
posSpring = r->posSpring;
|
|
negSpring = r->negSpring;
|
|
rotationSpringConstant = r->rotationSpringConstant;
|
|
rotationDampingConstant = r->rotationDampingConstant;
|
|
rotationPosSpring = r->rotationPosSpring;
|
|
rotationNegSpring = r->rotationNegSpring;
|
|
|
|
damageMultiplier[0] = r->collisionDamageMultiplier;
|
|
damageMultiplier[1] = r->ballisticDamageMultiplier;
|
|
damageMultiplier[2] = r->explosiveDamageMultiplier;
|
|
damageMultiplier[3] = r->laserDamageMultiplier;
|
|
damageMultiplier[4] = r->energyDamageMultiplier;
|
|
|
|
damageResponse[0] = r->collisionDamageResponse;
|
|
damageResponse[1] = r->ballisticDamageResponse;
|
|
damageResponse[2] = r->explosiveDamageResponse;
|
|
damageResponse[3] = r->laserDamageResponse;
|
|
damageResponse[4] = r->energyDamageResponse;
|
|
|
|
//
|
|
// The clamps are ctor-time COPIES of the spring pairs; the body pair is
|
|
// doubled. Every accumulator zeroes -- the binary leaves nothing to
|
|
// allocator fill (the donor's history records a NaN poison from exactly
|
|
// that omission).
|
|
//
|
|
eyeClampUpper = posSpring;
|
|
eyeClampLower = negSpring;
|
|
bodyClampUpper.Multiply(rotationPosSpring, 2.0f);
|
|
bodyClampLower.Multiply(rotationNegSpring, 2.0f);
|
|
|
|
{
|
|
Vector3D
|
|
vzero(0.0f, 0.0f, 0.0f);
|
|
eyePosition = eyeForce = eyeVelocity = eyeWork = vzero;
|
|
bodyOrientation = bodyForce = bodyVelocity = bodyWork = vzero;
|
|
placePos = placeRot = vzero;
|
|
placeQuat[0] = placeQuat[1] = placeQuat[2] = 0.0f;
|
|
placeQuat[3] = 1.0f;
|
|
|
|
int i;
|
|
for (i = 0; i < 12; ++i)
|
|
{
|
|
workMatrix[i] = 0.0f;
|
|
}
|
|
workMatrix[0] = workMatrix[5] = workMatrix[10] = 1.0f;
|
|
|
|
spare0 = 0.0f;
|
|
externalPitchPtr = &spare0;
|
|
}
|
|
vibrationDirection = Vector3D(0.0f, 1.0f, 0.0f);
|
|
swayAngle = 0.0f;
|
|
swayVelocity = 0.0f;
|
|
swayActive = 0;
|
|
swayBias = 0.0f;
|
|
|
|
eyeJointNode = owner->ResolveJoint(r->eyeJoint);
|
|
mechJointNode = owner->ResolveJoint(r->mechJoint);
|
|
|
|
//
|
|
// The master instance runs the gyro per-frame.
|
|
//
|
|
if (owner->GetInstance() != Entity::ReplicantInstance)
|
|
{
|
|
SetPerformance(&Gyroscope::GyroscopeSimulation);
|
|
}
|
|
|
|
Check_Fpu();
|
|
}
|
|
|
|
Gyroscope::~Gyroscope()
|
|
{
|
|
}
|
|
|
|
Logical
|
|
Gyroscope::TestClass(Mech &)
|
|
{
|
|
return True;
|
|
}
|
|
|
|
Logical
|
|
Gyroscope::TestInstance() const
|
|
{
|
|
return IsDerivedFrom(ClassDerivations);
|
|
}
|
|
|
|
void
|
|
Gyroscope::ResetToInitialState()
|
|
{
|
|
Check(this);
|
|
{
|
|
Vector3D
|
|
vzero(0.0f, 0.0f, 0.0f);
|
|
eyePosition = eyeForce = eyeVelocity = eyeWork = vzero;
|
|
bodyOrientation = bodyForce = bodyVelocity = bodyWork = vzero;
|
|
swayAngle = 0.0f;
|
|
}
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// @004b275c -- the per-frame gyro. Chain the power watch, slew the sway
|
|
// angle toward the powered or impaired percentage, and run both spring
|
|
// integrators. THE PERFORMANCE ENDS THERE: the joint writes are called from
|
|
// the MECH master performance tail after the gait, not from here.
|
|
//#############################################################################
|
|
//
|
|
void
|
|
Gyroscope::GyroscopeSimulation(Scalar time_slice)
|
|
{
|
|
Check(this);
|
|
|
|
PowerWatcher::Simulation(time_slice);
|
|
|
|
//
|
|
// The impaired test reads the gyro's OWN mirrored alarms -- the two
|
|
// UpdateWatch just drove: the voltage watchdog and the heat watch.
|
|
//
|
|
Logical
|
|
impaired =
|
|
watchdogAlarm.GetLevel() != PoweredSubsystem::Ready ||
|
|
heatAlarm.GetLevel() == HeatSink::FailureHeat;
|
|
|
|
Scalar
|
|
target =
|
|
(impaired ? percentageOnDestruction : percentageOnNormal)
|
|
+ swayBias;
|
|
|
|
Scalar
|
|
step = rotationPerSecond * time_slice;
|
|
if (target < swayAngle)
|
|
{
|
|
step = -step;
|
|
}
|
|
swayAngle += step;
|
|
|
|
if (step <= 0.0f)
|
|
{
|
|
if (step < 0.0f && swayAngle < target)
|
|
{
|
|
swayAngle = target;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (swayAngle > target)
|
|
{
|
|
swayAngle = target;
|
|
}
|
|
}
|
|
|
|
if (swayAngle > maxAnimationNoise)
|
|
{
|
|
swayAngle = maxAnimationNoise;
|
|
}
|
|
if (swayAngle < minAnimationNoise)
|
|
{
|
|
swayAngle = minAnimationNoise;
|
|
}
|
|
|
|
IntegrateEyeJoint(time_slice);
|
|
IntegrateBody(time_slice);
|
|
|
|
Check_Fpu();
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// @004b2ec0 -- the eye TRANSLATION spring, byte-exact quirks preserved: both
|
|
// spring terms use springConstant; the damping step OVERWRITES the force
|
|
// accumulator (it carries last frame's damping + damage impulses by design);
|
|
// and the position step has NO dt. Equilibrium is (posSpring+negSpring)/2
|
|
// per axis -- the authentic steady eye offset.
|
|
//#############################################################################
|
|
//
|
|
void
|
|
Gyroscope::IntegrateEyeJoint(Scalar time_slice)
|
|
{
|
|
Vector3D
|
|
toward_negative,
|
|
toward_positive,
|
|
force;
|
|
|
|
toward_negative.Subtract(eyePosition, negSpring);
|
|
toward_positive.Subtract(eyePosition, posSpring);
|
|
|
|
force.Multiply(springConstant, toward_negative);
|
|
eyeForce += force;
|
|
force.Multiply(springConstant, toward_positive);
|
|
eyeForce += force;
|
|
|
|
eyeWork = eyeForce;
|
|
force.Multiply(eyeWork, time_slice);
|
|
eyeVelocity += force;
|
|
|
|
eyeForce.Multiply(dampingConstant, eyeVelocity);
|
|
eyeWork = eyeForce;
|
|
force.Multiply(eyeWork, time_slice);
|
|
eyeVelocity += force;
|
|
|
|
eyePosition += eyeVelocity;
|
|
|
|
if (eyePosition.x > eyeClampUpper.x) eyePosition.x = eyeClampUpper.x;
|
|
if (eyePosition.y > eyeClampUpper.y) eyePosition.y = eyeClampUpper.y;
|
|
if (eyePosition.z > eyeClampUpper.z) eyePosition.z = eyeClampUpper.z;
|
|
if (eyePosition.x < eyeClampLower.x) eyePosition.x = eyeClampLower.x;
|
|
if (eyePosition.y < eyeClampLower.y) eyePosition.y = eyeClampLower.y;
|
|
if (eyePosition.z < eyeClampLower.z) eyePosition.z = eyeClampLower.z;
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// @004b30ec -- the body ROTATION spring. Same shape with three verified
|
|
// quirks: the spring force is X/Z-CROSSED (k.x*d.z, k.y*d.y, k.z*d.x); the
|
|
// damping is componentwise UNcrossed; and the orientation step crosses AGAIN
|
|
// (x += v.z, z += v.x).
|
|
//#############################################################################
|
|
//
|
|
void
|
|
Gyroscope::IntegrateBody(Scalar time_slice)
|
|
{
|
|
Vector3D
|
|
toward_negative,
|
|
toward_positive,
|
|
force;
|
|
|
|
toward_negative.Subtract(bodyOrientation, rotationNegSpring);
|
|
toward_positive.Subtract(bodyOrientation, rotationPosSpring);
|
|
|
|
force.x = rotationSpringConstant.x * toward_negative.z;
|
|
force.y = rotationSpringConstant.y * toward_negative.y;
|
|
force.z = rotationSpringConstant.z * toward_negative.x;
|
|
bodyForce += force;
|
|
force.x = rotationSpringConstant.x * toward_positive.z;
|
|
force.y = rotationSpringConstant.y * toward_positive.y;
|
|
force.z = rotationSpringConstant.z * toward_positive.x;
|
|
bodyForce += force;
|
|
|
|
bodyWork = bodyForce;
|
|
force.Multiply(bodyWork, time_slice);
|
|
bodyVelocity += force;
|
|
|
|
bodyForce.Multiply(rotationDampingConstant, bodyVelocity);
|
|
bodyWork = bodyForce;
|
|
force.Multiply(bodyWork, time_slice);
|
|
bodyVelocity += force;
|
|
|
|
bodyOrientation.x += bodyVelocity.z;
|
|
bodyOrientation.y += bodyVelocity.y;
|
|
bodyOrientation.z += bodyVelocity.x;
|
|
|
|
if (bodyOrientation.y > bodyClampUpper.y) bodyOrientation.y = bodyClampUpper.y;
|
|
if (bodyOrientation.x > bodyClampUpper.x) bodyOrientation.x = bodyClampUpper.x;
|
|
if (bodyOrientation.z > bodyClampUpper.z) bodyOrientation.z = bodyClampUpper.z;
|
|
if (bodyOrientation.y < bodyClampLower.y) bodyOrientation.y = bodyClampLower.y;
|
|
if (bodyOrientation.x < bodyClampLower.x) bodyOrientation.x = bodyClampLower.x;
|
|
if (bodyOrientation.z < bodyClampLower.z) bodyOrientation.z = bodyClampLower.z;
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// @004b33e0 / @004b34ec -- the joint writes, called from the MECH master
|
|
// performance tail after the gait pass.
|
|
//
|
|
// WriteEyeJoint scales the EyeJoint's own channel by swayAngle -- the idle
|
|
// wander. WriteMechJoint pushes the integrated state onto 'jointeye'
|
|
// (BallTranslation ONLY): TRANSLATION <- eyePosition, ROTATION <-
|
|
// bodyOrientation, each write gated on a real change.
|
|
//#############################################################################
|
|
//
|
|
|
|
static const Scalar
|
|
GyroQuantiseEps = 0.0001f;
|
|
|
|
void
|
|
Gyroscope::WriteEyeJoint()
|
|
{
|
|
Check(this);
|
|
|
|
if (eyeJointNode == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
switch (eyeJointNode->GetJointType())
|
|
{
|
|
case Joint::HingeXJointType:
|
|
case Joint::HingeYJointType:
|
|
case Joint::HingeZJointType:
|
|
{
|
|
Scalar
|
|
base = (Scalar)eyeJointNode->GetRadians(),
|
|
swayed = swayAngle * base;
|
|
if (
|
|
base - swayed > GyroQuantiseEps ||
|
|
swayed - base > GyroQuantiseEps
|
|
)
|
|
{
|
|
eyeJointNode->SetRotation(Radian(swayed));
|
|
}
|
|
}
|
|
break;
|
|
|
|
case Joint::BallJointType:
|
|
case Joint::BallTranslationJointType:
|
|
{
|
|
EulerAngles
|
|
base = eyeJointNode->GetEulerAngles();
|
|
EulerAngles
|
|
swayed(
|
|
Radian((Scalar)base.pitch * swayAngle),
|
|
Radian((Scalar)base.yaw * swayAngle),
|
|
Radian((Scalar)base.roll * swayAngle));
|
|
eyeJointNode->SetRotation(swayed);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void
|
|
Gyroscope::WriteMechJoint()
|
|
{
|
|
Check(this);
|
|
|
|
if (
|
|
mechJointNode == NULL ||
|
|
mechJointNode->GetJointType() != Joint::BallTranslationJointType
|
|
)
|
|
{
|
|
return;
|
|
}
|
|
|
|
{
|
|
Point3D
|
|
current = mechJointNode->GetTranslation();
|
|
if (
|
|
current.x - eyePosition.x > GyroQuantiseEps ||
|
|
eyePosition.x - current.x > GyroQuantiseEps ||
|
|
current.y - eyePosition.y > GyroQuantiseEps ||
|
|
eyePosition.y - current.y > GyroQuantiseEps ||
|
|
current.z - eyePosition.z > GyroQuantiseEps ||
|
|
eyePosition.z - current.z > GyroQuantiseEps
|
|
)
|
|
{
|
|
mechJointNode->SetTranslation(
|
|
Point3D(eyePosition.x, eyePosition.y, eyePosition.z));
|
|
}
|
|
}
|
|
{
|
|
EulerAngles
|
|
current = mechJointNode->GetEulerAngles();
|
|
if (
|
|
(Scalar)current.pitch - bodyOrientation.x > GyroQuantiseEps ||
|
|
bodyOrientation.x - (Scalar)current.pitch > GyroQuantiseEps ||
|
|
(Scalar)current.yaw - bodyOrientation.y > GyroQuantiseEps ||
|
|
bodyOrientation.y - (Scalar)current.yaw > GyroQuantiseEps ||
|
|
(Scalar)current.roll - bodyOrientation.z > GyroQuantiseEps ||
|
|
bodyOrientation.z - (Scalar)current.roll > GyroQuantiseEps
|
|
)
|
|
{
|
|
mechJointNode->SetRotation(
|
|
EulerAngles(
|
|
Radian(bodyOrientation.x),
|
|
Radian(bodyOrientation.y),
|
|
Radian(bodyOrientation.z)));
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// The hit-bounce kicks (@004b2d8c / @004b2de4 / @004b2e50): negate the hit
|
|
// direction, scale by exageration, land in the force accumulators. The
|
|
// torque zeroes its .y then negates the WHOLE vector; the vertical kick
|
|
// keeps only the pitch component and mirrors it into .y.
|
|
//#############################################################################
|
|
//
|
|
void
|
|
Gyroscope::ApplyDamageImpulse(Scalar x, Scalar y, Scalar z, Scalar magnitude)
|
|
{
|
|
Check(this);
|
|
|
|
Vector3D
|
|
direction(-x, -y, -z);
|
|
magnitude *= exageration;
|
|
direction *= magnitude;
|
|
eyeForce += direction;
|
|
}
|
|
|
|
void
|
|
Gyroscope::ApplyDamageTorque(Scalar x, Scalar y, Scalar z, Scalar magnitude)
|
|
{
|
|
Check(this);
|
|
|
|
Vector3D
|
|
direction(-x, -y, -z);
|
|
magnitude *= exageration;
|
|
direction *= magnitude;
|
|
bodyForce += direction;
|
|
bodyForce.y = 0.0f;
|
|
bodyForce.Negate(bodyForce);
|
|
}
|
|
|
|
void
|
|
Gyroscope::ApplyVerticalImpulse(Scalar pitch, Scalar magnitude)
|
|
{
|
|
Check(this);
|
|
|
|
Vector3D
|
|
direction(pitch, 0.0f, 0.0f);
|
|
magnitude *= exageration;
|
|
direction *= magnitude;
|
|
bodyForce += direction;
|
|
bodyForce.y = bodyForce.x;
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// @004b2980 -- the damage fan-out: every non-collision hit shakes the
|
|
// cockpit. Direction = the record's damageForce, or a random horizontal
|
|
// when it is ~zero; rotated into the yaw-only torso frame and re-normalized;
|
|
// per-type scaled amount / multiplier * response (Explosive alone reads
|
|
// burstCount); each channel clamped at 1.3 UPPER only; then the four kicks.
|
|
//#############################################################################
|
|
//
|
|
void
|
|
Gyroscope::ApplyDamageResponse(const Damage &damage)
|
|
{
|
|
Check(this);
|
|
|
|
if (damage.damageAmount <= 0.0f)
|
|
{
|
|
return;
|
|
}
|
|
if (damage.damageType == Damage::CollisionDamageType)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Scalar
|
|
trans = 0.0f,
|
|
pitch_roll = 0.0f,
|
|
yaw = 0.0f,
|
|
vibration = 0.0f;
|
|
|
|
Vector3D
|
|
direction;
|
|
if (
|
|
damage.damageForce.x < 1.0e-4f && damage.damageForce.x > -1.0e-4f &&
|
|
damage.damageForce.y < 1.0e-4f && damage.damageForce.y > -1.0e-4f &&
|
|
damage.damageForce.z < 1.0e-4f && damage.damageForce.z > -1.0e-4f
|
|
)
|
|
{
|
|
direction.x = ((Scalar)Random >= 0.5f) ? (Scalar)Random : -(Scalar)Random;
|
|
direction.z = ((Scalar)Random >= 0.5f) ? (Scalar)Random : -(Scalar)Random;
|
|
direction.y = 0.0f;
|
|
}
|
|
else
|
|
{
|
|
direction = damage.damageForce;
|
|
}
|
|
direction.Normalize(direction);
|
|
|
|
//
|
|
// Rotate the WORLD hit direction into the yaw-only body frame (the
|
|
// torso twist), then re-normalize.
|
|
//
|
|
placeRot = Vector3D(0.0f, *externalPitchPtr, 0.0f);
|
|
placePos = Vector3D(0.0f, 0.0f, 0.0f);
|
|
{
|
|
AffineMatrix
|
|
frame;
|
|
frame = EulerAngles(
|
|
Radian(placeRot.x), Radian(placeRot.y), Radian(placeRot.z));
|
|
int
|
|
i;
|
|
for (i = 0; i < 12; ++i)
|
|
{
|
|
workMatrix[i] = frame.entries[i];
|
|
}
|
|
Vector3D
|
|
world = direction;
|
|
direction.x =
|
|
world.x * workMatrix[0] + world.y * workMatrix[1] +
|
|
world.z * workMatrix[2];
|
|
direction.y =
|
|
world.x * workMatrix[4] + world.y * workMatrix[5] +
|
|
world.z * workMatrix[6];
|
|
direction.z =
|
|
world.x * workMatrix[8] + world.y * workMatrix[9] +
|
|
world.z * workMatrix[10];
|
|
}
|
|
direction.Normalize(direction);
|
|
|
|
switch (damage.damageType)
|
|
{
|
|
case Damage::BallisticDamageType:
|
|
trans = damage.damageAmount / damageMultiplier[1] * damageResponse[1].trans;
|
|
pitch_roll = 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:
|
|
trans = (Scalar)damage.burstCount * damage.damageAmount / damageMultiplier[2] * damageResponse[2].trans;
|
|
pitch_roll = (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:
|
|
trans = damage.damageAmount / damageMultiplier[3] * damageResponse[3].trans;
|
|
pitch_roll = 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:
|
|
trans = damage.damageAmount / damageMultiplier[4] * damageResponse[4].trans;
|
|
pitch_roll = 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;
|
|
}
|
|
|
|
if (trans > 1.3f) trans = 1.3f;
|
|
if (pitch_roll > 1.3f) pitch_roll = 1.3f;
|
|
if (yaw > 1.3f) yaw = 1.3f;
|
|
if (vibration > 1.3f) vibration = 1.3f;
|
|
|
|
ApplyDamageImpulse(direction.x, direction.y, direction.z, trans);
|
|
ApplyDamageTorque(direction.x, direction.y, direction.z, pitch_roll);
|
|
ApplyDamageImpulse(
|
|
vibrationDirection.x, vibrationDirection.y, vibrationDirection.z,
|
|
vibration);
|
|
ApplyVerticalImpulse(yaw, yaw);
|
|
}
|