Reconstructs Torso::TorsoSimulation (installed as the Torso's per-frame Performance) and wires the torso aim into MechControlsMapper::InterpretControls, so the control surface now covers aiming as well as locomotion. - TORSO: analogElevationAxis/analogTwistAxis + SetAnalog* setters, CurrentElevation()/GetHorizontalEnabled() accessors. TorsoSimulation slews currentElevation += analogElevationAxis*baseElevationRate*dt clamped to the vertical limits, and (if horizontalEnabled) currentTwist by the twist axis clamped to the horizontal limits (authentic torso.cpp core; the skeleton-joint application is deferred with the render wave). - InterpretControls: routes stick pitch (stick_y, squared) into the torso elevation every mode; Std/Vet route stick yaw into the twist. BT_FORCE_ELEV dev hook. Verified: BT_FORCE_ELEV=0.8 -> the elevation slews up and clamps at the authentic verticalLimitTop = 0.349 rad = 20deg; neutral -> 0; zero Fail. Deferred: skeleton-joint application (render wave), HUD free-aim slew, look/eyepoint commit. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
301 lines
8.9 KiB
C++
301 lines
8.9 KiB
C++
//===========================================================================//
|
|
// File: mechmppr.cpp //
|
|
// Project: BattleTech //
|
|
// Contents: Implementation details for the mech controls mapper //
|
|
//---------------------------------------------------------------------------//
|
|
// 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(MECHMPPR_HPP)
|
|
# include <mechmppr.hpp>
|
|
#endif
|
|
|
|
#if !defined(MECH_HPP)
|
|
# include <mech.hpp>
|
|
#endif
|
|
|
|
#if !defined(TORSO_HPP)
|
|
# include <torso.hpp>
|
|
#endif
|
|
|
|
Derivation
|
|
MechControlsMapper::ClassDerivations(
|
|
Subsystem::ClassDerivations,
|
|
"MechControlsMapper"
|
|
);
|
|
|
|
//
|
|
//#############################################################################
|
|
// Published control-input attributes. The streamed control mappings bind (by
|
|
// name) to these; the pad entry fills the chain-vs-id gap at id 2.
|
|
//#############################################################################
|
|
//
|
|
const MechControlsMapper::IndexEntry
|
|
MechControlsMapper::AttributePointers[]=
|
|
{
|
|
{ (int)MechControlsMapper::MechControlsMapperPadFirstAttributeID,
|
|
"MechControlsMapperPad02",
|
|
(Simulation::AttributePointer)&MechControlsMapper::throttlePosition },
|
|
ATTRIBUTE_ENTRY(MechControlsMapper, StickPosition, stickPosition),
|
|
ATTRIBUTE_ENTRY(MechControlsMapper, ThrottlePosition, throttlePosition),
|
|
ATTRIBUTE_ENTRY(MechControlsMapper, PedalsPosition, pedalsPosition),
|
|
ATTRIBUTE_ENTRY(MechControlsMapper, ReverseThrust, reverseThrust),
|
|
ATTRIBUTE_ENTRY(MechControlsMapper, SpeedDemand, speedDemand),
|
|
ATTRIBUTE_ENTRY(MechControlsMapper, TurnDemand, turnDemand),
|
|
ATTRIBUTE_ENTRY(MechControlsMapper, LookForward, lookForward),
|
|
ATTRIBUTE_ENTRY(MechControlsMapper, LookLeft, lookLeft),
|
|
ATTRIBUTE_ENTRY(MechControlsMapper, LookRight, lookRight),
|
|
ATTRIBUTE_ENTRY(MechControlsMapper, LookBehind, lookBehind),
|
|
ATTRIBUTE_ENTRY(MechControlsMapper, LookDown, lookDown),
|
|
ATTRIBUTE_ENTRY(MechControlsMapper, TorsoUp, torsoUp),
|
|
ATTRIBUTE_ENTRY(MechControlsMapper, TorsoDown, torsoDown),
|
|
ATTRIBUTE_ENTRY(MechControlsMapper, TorsoLeft, torsoLeft),
|
|
ATTRIBUTE_ENTRY(MechControlsMapper, TorsoRight, torsoRight),
|
|
ATTRIBUTE_ENTRY(MechControlsMapper, TorsoCenter, torsoCenter),
|
|
ATTRIBUTE_ENTRY(MechControlsMapper, ControlMode, controlMode),
|
|
ATTRIBUTE_ENTRY(MechControlsMapper, DisplayMode, displayMode),
|
|
ATTRIBUTE_ENTRY(MechControlsMapper, PilotArrayPage, pilotArrayPage),
|
|
ATTRIBUTE_ENTRY(MechControlsMapper, PilotArray, pilotArray)
|
|
};
|
|
|
|
MechControlsMapper::AttributeIndexSet
|
|
MechControlsMapper::AttributeIndex(
|
|
ELEMENTS(MechControlsMapper::AttributePointers),
|
|
MechControlsMapper::AttributePointers,
|
|
Subsystem::AttributeIndex
|
|
);
|
|
|
|
MechControlsMapper::SharedData
|
|
MechControlsMapper::DefaultData(
|
|
MechControlsMapper::ClassDerivations,
|
|
Subsystem::MessageHandlers,
|
|
MechControlsMapper::AttributeIndex,
|
|
1
|
|
);
|
|
|
|
MechControlsMapper::MechControlsMapper(
|
|
Mech *owner,
|
|
int subsystem_ID,
|
|
CString subsystem_name,
|
|
RegisteredClass::ClassID class_ID,
|
|
SharedData &shared_data
|
|
):
|
|
Subsystem(
|
|
(Entity *)owner,
|
|
subsystem_ID,
|
|
subsystem_name,
|
|
class_ID,
|
|
shared_data
|
|
)
|
|
{
|
|
Check(owner);
|
|
//
|
|
// Prime the published control inputs to neutral. Per-frame interpretation
|
|
// (InterpretControls) is reconstructed with the mech2/3/4 sim (phase 5.3).
|
|
//
|
|
stickPosition.x = 0.0f;
|
|
stickPosition.y = 0.0f;
|
|
throttlePosition = 0.0f;
|
|
pedalsPosition = 0.0f;
|
|
reverseThrust = 0;
|
|
speedDemand = 0.0f;
|
|
turnDemand = 0.0f;
|
|
lookForward = 0;
|
|
lookLeft = 0;
|
|
lookRight = 0;
|
|
lookBehind = 0;
|
|
lookDown = 0;
|
|
torsoUp = 0;
|
|
torsoDown = 0;
|
|
torsoLeft = 0;
|
|
torsoRight = 0;
|
|
torsoCenter = 0;
|
|
controlMode = BasicMode;
|
|
displayMode = 0;
|
|
pilotArrayPage = 0;
|
|
pilotArray = 0;
|
|
{
|
|
for (int i = 0; i < 24; ++i)
|
|
{
|
|
reserved[i] = 0;
|
|
}
|
|
}
|
|
//
|
|
// Install the per-frame control-interpretation Performance.
|
|
//
|
|
SetPerformance(&MechControlsMapper::InterpretControls);
|
|
|
|
if (getenv("BT_MECH_LOG"))
|
|
{
|
|
DEBUG_STREAM << "[mapper] ctor id=" << subsystem_ID
|
|
<< " throttleAttr=" << GetAttributePointer(ThrottlePositionAttributeID)
|
|
<< " controlModeAttr=" << GetAttributePointer(ControlModeAttributeID)
|
|
<< endl << flush;
|
|
}
|
|
Check_Fpu();
|
|
}
|
|
|
|
MechControlsMapper::~MechControlsMapper()
|
|
{
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// InterpretControls -- the mapper's per-frame Performance. Reads the raw input
|
|
// attributes (the engine controls push refreshes throttle/stick/pedals/buttons
|
|
// from the RIO/keyboard before this runs) and publishes the locomotion demands
|
|
// (speedDemand in world-u/s, turnDemand [-1..1]) the mech drive consumes.
|
|
//
|
|
// Reconstructs the authentic demand math (mechmppr.cpp @004afd10):
|
|
// * throttle -> forward speed: speedDemand = topSpeed * throttle * fwdScale
|
|
// (reverse thrust inverts and drops the forward scale);
|
|
// * soft stick response: square the yaw (sign preserved), cube the pedals;
|
|
// * Basic: stick yaw = turn; Standard/Veteran: pedals = turn;
|
|
// * speed is clamped down while turning hard (max_turn ramp).
|
|
//
|
|
// DEV hook (headless verification, no RIO/keyboard): BT_FORCE_THROTTLE /
|
|
// BT_FORCE_TURN override the pushed raw inputs so the demand math + mech drive
|
|
// are exercisable without hardware. The torso-aim / free-look interpretation
|
|
// (torso analog axes, look/eyepoint commit) is the aiming wave, deferred.
|
|
//#############################################################################
|
|
//
|
|
void
|
|
MechControlsMapper::InterpretControls(Scalar time_slice)
|
|
{
|
|
Check(this);
|
|
|
|
Mech *mech = GetMech();
|
|
Check(mech);
|
|
|
|
//
|
|
// DEV forced-input override.
|
|
//
|
|
{
|
|
const char *force_throttle = getenv("BT_FORCE_THROTTLE");
|
|
if (force_throttle != NULL)
|
|
{
|
|
Scalar t = (Scalar)atof(force_throttle);
|
|
throttlePosition = (t >= 0.0f) ? t : -t;
|
|
reverseThrust = (t < 0.0f) ? 1 : 0;
|
|
}
|
|
const char *force_turn = getenv("BT_FORCE_TURN");
|
|
if (force_turn != NULL)
|
|
{
|
|
stickPosition.x = (Scalar)atof(force_turn);
|
|
}
|
|
const char *force_elev = getenv("BT_FORCE_ELEV");
|
|
if (force_elev != NULL)
|
|
{
|
|
stickPosition.y = (Scalar)atof(force_elev);
|
|
}
|
|
}
|
|
|
|
Torso *torso = (Torso *)mech->GetTorsoSubsystem();
|
|
|
|
Scalar topSpeed = mech->GetReverseStrideLength();
|
|
Scalar walkSpeed = mech->GetWalkStrideLength();
|
|
|
|
//
|
|
// Throttle -> forward speed demand.
|
|
//
|
|
if (reverseThrust < 1)
|
|
{
|
|
speedDemand = topSpeed * throttlePosition * mech->GetForwardThrottleScale();
|
|
}
|
|
else
|
|
{
|
|
speedDemand = -topSpeed * throttlePosition;
|
|
}
|
|
|
|
//
|
|
// Soft response: square the stick yaw (sign preserved), cube the pedals.
|
|
//
|
|
Scalar stick_x = stickPosition.x * stickPosition.x;
|
|
if (stickPosition.x < 0.0f)
|
|
{
|
|
stick_x = -stick_x;
|
|
}
|
|
Scalar stick_y = stickPosition.y * stickPosition.y;
|
|
if (stickPosition.y < 0.0f)
|
|
{
|
|
stick_y = -stick_y;
|
|
}
|
|
Scalar pedal_3 = pedalsPosition * pedalsPosition * pedalsPosition;
|
|
|
|
turnDemand = 0.0f;
|
|
|
|
//
|
|
// Torso aim. Every control mode routes the stick pitch (stick_y) into the
|
|
// torso weapon-elevation axis. In Basic the stick yaw is the turn (legs);
|
|
// in Standard/Veteran the stick yaw is the torso twist (free aim) and the
|
|
// pedals steer. (The HUD free-aim slew + look/eyepoint commit are the
|
|
// aiming/camera wave, deferred.)
|
|
//
|
|
if (controlMode == BasicMode)
|
|
{
|
|
turnDemand = stick_x;
|
|
if (torso != NULL)
|
|
{
|
|
torso->SetAnalogElevationAxis(stick_y);
|
|
torso->SetAnalogTwistAxis(0.0f);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
turnDemand = pedal_3;
|
|
if (torso != NULL)
|
|
{
|
|
torso->SetAnalogElevationAxis(stick_y);
|
|
torso->SetAnalogTwistAxis(stick_x);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Clamp forward speed down while turning hard (except VeteranMode, which has
|
|
// no speed-dependent turn clamp). max_turn ramps from topSpeed (no turn) to
|
|
// walkSpeed (full turn).
|
|
//
|
|
if (controlMode != VeteranMode)
|
|
{
|
|
Scalar turn_mag = (turnDemand < 0.0f) ? -turnDemand : turnDemand;
|
|
if (turn_mag > 0.001f)
|
|
{
|
|
Scalar max_turn = (topSpeed - walkSpeed) * (1.0f - turn_mag) + walkSpeed;
|
|
if (speedDemand > max_turn) speedDemand = max_turn;
|
|
if (speedDemand < -max_turn) speedDemand = -max_turn;
|
|
}
|
|
}
|
|
|
|
if (getenv("BT_MECH_LOG"))
|
|
{
|
|
static Scalar reportAccum = 0.0f;
|
|
reportAccum += time_slice;
|
|
if (reportAccum >= 1.0f)
|
|
{
|
|
reportAccum = 0.0f;
|
|
DEBUG_STREAM << "[mppr] thr=" << throttlePosition
|
|
<< " rev=" << reverseThrust
|
|
<< " stickX=" << stickPosition.x
|
|
<< " stickY=" << stickPosition.y
|
|
<< " mode=" << controlMode
|
|
<< " -> speedDemand=" << speedDemand
|
|
<< " turnDemand=" << turnDemand
|
|
<< " torsoElev=" << (torso ? torso->CurrentElevation() : 0.0f)
|
|
<< endl << flush;
|
|
}
|
|
}
|
|
|
|
Check_Fpu();
|
|
}
|
|
|
|
Logical
|
|
MechControlsMapper::TestInstance() const
|
|
{
|
|
return IsDerivedFrom(ClassDerivations);
|
|
}
|