The linker map named the faulting function: the crash address minus the CODE base (0x410000) looked up in btl4opt.map's Publics-by-Value landed inside AudioStateWatcher::AudioStateWatcher +0x2D. AudioStateWatcher is AudioWatcherOf<StateIndicator> and its ctor immediately runs Cast_Object(StateIndicator*, attributePointer)->AddAudioWatcher(this). So every authored *State name must be published as a StateIndicator -- AlarmIndicator counts, it derives from one -- and pointing one at a plain int sends that member call through garbage. That explains both earlier failures: the AlarmIndicator attempt was the right type but was tested with other bugs still in the batch, and the plain-int attempt was simply the wrong type and crashed further along. Published as state objects: Reservoir/ReservoirState -> reservoirAlarm, Generator/GeneratorState -> stateAlarm, plus new StateIndicator members for Condenser/CondenserState and Torso/MotionState. StateIndicator has no Initialize(); the default ctor suffices because the watcher only needs the object to exist. Verified on the pod rig: no crash, ladder advanced to ReportLeak. Technique worth keeping: on an extender fault, subtract 0x410000 from the dumped address and look it up in btl4opt.map -- it names the engine function, and for this family of work that names the watcher class and hence the required member type. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
222 lines
6.6 KiB
C++
222 lines
6.6 KiB
C++
//===========================================================================//
|
|
// File: torso.cpp //
|
|
// Project: BattleTech Brick: Mech subsystems //
|
|
// Contents: Torso -- torso twist / weapon elevation //
|
|
//---------------------------------------------------------------------------//
|
|
// 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(TORSO_HPP)
|
|
# include <torso.hpp>
|
|
#endif
|
|
|
|
#if !defined(MECH_HPP)
|
|
# include <mech.hpp>
|
|
#endif
|
|
|
|
#if !defined(JOINT_HPP)
|
|
# include <joint.hpp>
|
|
#endif
|
|
|
|
Derivation
|
|
Torso::ClassDerivations(
|
|
PowerWatcher::ClassDerivations,
|
|
"Torso"
|
|
);
|
|
|
|
const Torso::IndexEntry
|
|
Torso::AttributePointers[]=
|
|
{
|
|
ATTRIBUTE_ENTRY(Torso, RotationOfTorsoVertical, currentElevation),
|
|
ATTRIBUTE_ENTRY(Torso, RotationOfTorsoHorizontal, currentTwist),
|
|
ATTRIBUTE_ENTRY(Torso, HorizontalLimitRight, horizontalLimitRight),
|
|
ATTRIBUTE_ENTRY(Torso, HorizontalLimitLeft, horizontalLimitLeft),
|
|
ATTRIBUTE_ENTRY(Torso, SpeedOfTorsoVertical, baseElevationRate),
|
|
ATTRIBUTE_ENTRY(Torso, SpeedOfTorsoHorizontal, baseTwistRate),
|
|
ATTRIBUTE_ENTRY(Torso, MotionState, motionState)
|
|
};
|
|
|
|
Torso::AttributeIndexSet
|
|
Torso::AttributeIndex(
|
|
ELEMENTS(Torso::AttributePointers),
|
|
Torso::AttributePointers,
|
|
Subsystem::AttributeIndex
|
|
);
|
|
|
|
Torso::SharedData
|
|
Torso::DefaultData(
|
|
Torso::ClassDerivations,
|
|
Subsystem::MessageHandlers,
|
|
Torso::AttributeIndex,
|
|
Subsystem::StateCount
|
|
);
|
|
|
|
Torso::Torso(
|
|
Mech *owner,
|
|
int subsystem_ID,
|
|
SubsystemResource *r,
|
|
SharedData &shared_data
|
|
):
|
|
PowerWatcher(owner, subsystem_ID, r, shared_data)
|
|
{
|
|
Check(owner);
|
|
Check_Pointer(r);
|
|
|
|
isDamagedCopy = (owner->GetInstance() == Entity::ReplicantInstance);
|
|
|
|
statusFlags = 0;
|
|
buttonAccelerationPerSecond = r->buttonAccelerationPerSecond;
|
|
buttonAccelerationStart = r->buttonAccelerationStartValue;
|
|
|
|
baseTwistRate = r->horizontalRotationPerSecond * RAD_PER_DEG;
|
|
baseElevationRate = r->verticalRotationPerSecond * RAD_PER_DEG;
|
|
horizontalLimitRight= r->horizontalLimitRight * RAD_PER_DEG;
|
|
horizontalLimitLeft = r->horizontalLimitLeft * RAD_PER_DEG;
|
|
verticalLimitTop = r->verticalLimitTop * RAD_PER_DEG;
|
|
verticalLimitBottom = r->verticalLimitBottom * RAD_PER_DEG;
|
|
|
|
twistCenterHigh = verticalLimitTop;
|
|
twistCenterLow = verticalLimitBottom;
|
|
elevationCenter = verticalLimitTop;
|
|
elevationHalfBottom = verticalLimitBottom * 0.5f;
|
|
|
|
buttonRampActive = 0;
|
|
buttonRamp = 0.0f;
|
|
horizontalEnabled = r->torsoHorizontalEnabled;
|
|
|
|
//
|
|
// Resolve the named torso-twist skeleton joints (twist body + shadow) from
|
|
// the mech skeleton (now live). A mech with a fixed torso, or one whose
|
|
// joint name is absent from the skeleton, resolves NULL and simply never
|
|
// twists.
|
|
//
|
|
horizontalJointNode = owner->ResolveJoint(r->torsoHorizontalJoint);
|
|
horizontalShadowJointNode = owner->ResolveJoint(r->torsoHorizontalShadowJoint);
|
|
|
|
if (getenv("BT_MECH_LOG"))
|
|
{
|
|
DEBUG_STREAM << "[torso] horizJoint '" << r->torsoHorizontalJoint
|
|
<< "' -> " << (void *)horizontalJointNode;
|
|
if (horizontalJointNode != NULL)
|
|
{
|
|
DEBUG_STREAM << " type=" << (int)horizontalJointNode->GetJointType();
|
|
}
|
|
DEBUG_STREAM << " enabled=" << (int)horizontalEnabled << endl << flush;
|
|
}
|
|
|
|
currentTwist = 0.0f;
|
|
currentElevation = 0.0f;
|
|
analogElevationAxis = 0.0f;
|
|
analogTwistAxis = 0.0f;
|
|
for (int i = 0; i < 22; ++i)
|
|
{
|
|
dynamicsState[i] = 0.0f;
|
|
}
|
|
|
|
//
|
|
// Install the per-frame twist/elevation Performance (the replicant copy is
|
|
// driven by console updates via TorsoCopySimulation instead, still staged).
|
|
//
|
|
if (owner->GetInstance() != Entity::ReplicantInstance)
|
|
{
|
|
SetPerformance(&Torso::TorsoSimulation);
|
|
}
|
|
|
|
Check_Fpu();
|
|
}
|
|
|
|
Torso::~Torso()
|
|
{
|
|
}
|
|
|
|
Logical
|
|
Torso::TestClass(Mech &)
|
|
{
|
|
return True;
|
|
}
|
|
|
|
Logical
|
|
Torso::TestInstance() const
|
|
{
|
|
return IsDerivedFrom(ClassDerivations);
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// TorsoSimulation -- per-frame torso twist / weapon-elevation integration.
|
|
//
|
|
// Slews the current elevation/twist toward the analog aim axes (written by
|
|
// MechControlsMapper::InterpretControls) at the resource base rates, clamped to
|
|
// the resource limits. Authentic core (torso.cpp @004b6xxx): current += axis *
|
|
// baseRate * dt. APPLYING the resolved angles onto the skeleton torso/gun
|
|
// joints (horizontalJointNode / the elevation joint) is deferred with the
|
|
// skeleton-link + render wave; here we advance the scalar aim state (read by the
|
|
// HUD reticle / weapon aim and, once wired, the joints).
|
|
//#############################################################################
|
|
//
|
|
void
|
|
Torso::TorsoSimulation(Scalar time_slice)
|
|
{
|
|
Check(this);
|
|
|
|
//
|
|
// Elevation (weapon pitch): slew toward the analog axis, clamp to limits.
|
|
//
|
|
currentElevation += analogElevationAxis * baseElevationRate * time_slice;
|
|
{
|
|
Scalar lo = (verticalLimitBottom < verticalLimitTop)
|
|
? verticalLimitBottom : verticalLimitTop;
|
|
Scalar hi = (verticalLimitBottom < verticalLimitTop)
|
|
? verticalLimitTop : verticalLimitBottom;
|
|
if (currentElevation < lo) currentElevation = lo;
|
|
if (currentElevation > hi) currentElevation = hi;
|
|
}
|
|
|
|
//
|
|
// Twist (horizontal torso rotation): only mechs with an enabled torso joint.
|
|
//
|
|
if (horizontalEnabled)
|
|
{
|
|
currentTwist += analogTwistAxis * baseTwistRate * time_slice;
|
|
Scalar lo = (horizontalLimitLeft < horizontalLimitRight)
|
|
? horizontalLimitLeft : horizontalLimitRight;
|
|
Scalar hi = (horizontalLimitLeft < horizontalLimitRight)
|
|
? horizontalLimitRight : horizontalLimitLeft;
|
|
if (currentTwist < lo) currentTwist = lo;
|
|
if (currentTwist > hi) currentTwist = hi;
|
|
|
|
//
|
|
// Push the twist onto the skeleton torso joint so the upper body rotates
|
|
// on the model. Hinge joints take the scalar angle about their axis;
|
|
// ball joints take it as the yaw component.
|
|
//
|
|
if (horizontalJointNode != NULL)
|
|
{
|
|
Joint::JointType jt = horizontalJointNode->GetJointType();
|
|
if (jt == Joint::HingeXJointType
|
|
|| jt == Joint::HingeYJointType
|
|
|| jt == Joint::HingeZJointType)
|
|
{
|
|
horizontalJointNode->SetRotation(Radian(currentTwist));
|
|
}
|
|
else if (jt == Joint::BallJointType)
|
|
{
|
|
horizontalJointNode->SetRotation(EulerAngles(0.0f, currentTwist, 0.0f));
|
|
}
|
|
}
|
|
}
|
|
|
|
Check_Fpu();
|
|
}
|
|
|
|
void
|
|
Torso::TorsoCopySimulation(Scalar)
|
|
{
|
|
Fail("Torso::TorsoCopySimulation -- torso.cpp not yet reconstructed");
|
|
}
|