BT410 5.3.112: the hot path stops paying for its own diagnostics -- plus the HUD's confident half
PERFORMANCE, measured not guessed (operator report: "vastly slower than the original"). The audit found and fixed: THE [crash] FLOOD: the collision-block log printed EVERY BLOCKED FRAME -- 5,487 lines in one wall-grinding run (~13/s sustained), each a float-heavy iostream format pushed through the COM3 serial redirect. The shipped exe prints nothing, ever. Now logs the CONTACT EDGE only (the same collisionState 0->1 gate the audio uses). ~100 UNCACHED getenv SCANS PER FRAME: every gated log site walked the DOS environment block per call, and the control mapper ran FOUR getenv + atof parses per frame in the input path whether logging was on or not. All hot-path gates now cache in function-local statics (the donor's own idiom); the force-input overrides parse once. Ruled out: the debug macros (DEBUG_LEVEL=0 nulls Check/Verify wholesale, DEBUGOFF.HPP) and optimization (-O2, the authentic OPT flags). MEASURED RESULT: crash lines 5487 -> 0 on the same mission; the SOS frame pacing is 16.67 ticks/frame with drift=0 in BOTH the flooded and fixed runs -- the fixed-tick sim never actually lost its clock on the rig, so the flood's cost was wall-time in the serial path, not sim drift. THE OTHER HALF OF THE ANSWER, for the operator: since 5.3.95 the hull's speed is STRIDE-DRIVEN. The mech steps off at ~7 u/s and winds up through the walk band (capped 18.5) to the run only across clip-boundary transitions -- seconds, like the real pod -- where the old bring-up model hit 26.9 instantly. Walls also now stop the mech dead. If "slower" means acceleration feel rather than frame rate, that is the AUTHENTIC gait arriving, not a defect. ALSO IN: HUD::HudSimulation's confident half (base tick, heat/voltage page gating off the 5.3.109 mirrors, the blink -- with >= on the flicker compare where the decomp shard's literal != would toggle every tick), registered on the master; the reticle/lock/torso-horizon math stays STAGED, matching the donor's own unfinished state. Stub census: 17 across 11 files. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
+173
-110
@@ -1,110 +1,173 @@
|
||||
//===========================================================================//
|
||||
// File: hud.cpp //
|
||||
// Project: BattleTech Brick: Mech subsystems //
|
||||
// Contents: HUD -- head-up display / targeting reticle //
|
||||
//---------------------------------------------------------------------------//
|
||||
// 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(HUD_HPP)
|
||||
# include <hud.hpp>
|
||||
#endif
|
||||
|
||||
#if !defined(MECH_HPP)
|
||||
# include <mech.hpp>
|
||||
#endif
|
||||
|
||||
static const Point3D HudZeroVector(0.0f, 0.0f, 0.0f);
|
||||
|
||||
Derivation
|
||||
HUD::ClassDerivations(
|
||||
PowerWatcher::ClassDerivations,
|
||||
"HUD"
|
||||
);
|
||||
|
||||
HUD::SharedData
|
||||
HUD::DefaultData(
|
||||
HUD::ClassDerivations,
|
||||
Subsystem::MessageHandlers,
|
||||
Subsystem::AttributeIndex,
|
||||
Subsystem::StateCount
|
||||
);
|
||||
|
||||
HUD::HUD(
|
||||
Mech *owner,
|
||||
int subsystem_ID,
|
||||
SubsystemResource *subsystem_resource,
|
||||
SharedData &shared_data
|
||||
):
|
||||
PowerWatcher(owner, subsystem_ID, subsystem_resource, shared_data)
|
||||
{
|
||||
Check(owner);
|
||||
Check_Pointer(subsystem_resource);
|
||||
|
||||
flickerRate = 0.0f;
|
||||
rotationOfTorsoHorizontal = 0.0f;
|
||||
torsoLimitRight = 0.0f;
|
||||
torsoLimitLeft = 0.0f;
|
||||
torsoSpeed = 0.0f;
|
||||
rangeToTarget = 0.0f;
|
||||
scratch1F0 = 0;
|
||||
visible = True;
|
||||
lockFlag = False;
|
||||
hotBoxVector = HudZeroVector;
|
||||
threatVector = HudZeroVector;
|
||||
compassHeading = 0.0f;
|
||||
blinkTimer = 0.0f;
|
||||
flickerTimer = 0.0f;
|
||||
blinkState = True;
|
||||
transitionFlag = False;
|
||||
previousHorizontal = 0.0f;
|
||||
|
||||
statusAlarm.Initialize(2);
|
||||
|
||||
freeAimSlew = 0.0f;
|
||||
scratch290 = 0.0f;
|
||||
horizontalTorsoOffset = 0.0f;
|
||||
|
||||
flickerRate = subsystem_resource->flickerRate;
|
||||
horizontalMovementPerSecond = subsystem_resource->horizontalMovementPerSecond;
|
||||
horizontalLimit = subsystem_resource->horizontalLimit;
|
||||
|
||||
//
|
||||
// The torso-orientation source is a Mech link not yet live at
|
||||
// construction time; HudSimulation refreshes these per-frame.
|
||||
//
|
||||
linkedEntity = 0;
|
||||
flickerActive = 0;
|
||||
|
||||
Check_Fpu();
|
||||
}
|
||||
|
||||
HUD::~HUD()
|
||||
{
|
||||
}
|
||||
|
||||
Logical
|
||||
HUD::TestClass(Mech &)
|
||||
{
|
||||
return True;
|
||||
}
|
||||
|
||||
Logical
|
||||
HUD::TestInstance() const
|
||||
{
|
||||
return IsDerivedFrom(ClassDerivations);
|
||||
}
|
||||
|
||||
//
|
||||
// Per-frame reticle / compass / lock-ring update. Not yet reconstructed.
|
||||
//
|
||||
void
|
||||
HUD::HudSimulation(Scalar)
|
||||
{
|
||||
Fail("HUD::HudSimulation -- hud.cpp not yet reconstructed");
|
||||
}
|
||||
//===========================================================================//
|
||||
// File: hud.cpp //
|
||||
// Project: BattleTech Brick: Mech subsystems //
|
||||
// Contents: HUD -- head-up display / targeting reticle //
|
||||
//---------------------------------------------------------------------------//
|
||||
// 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(HUD_HPP)
|
||||
# include <hud.hpp>
|
||||
#endif
|
||||
|
||||
#if !defined(MECH_HPP)
|
||||
# include <mech.hpp>
|
||||
#endif
|
||||
|
||||
static const Point3D HudZeroVector(0.0f, 0.0f, 0.0f);
|
||||
|
||||
Derivation
|
||||
HUD::ClassDerivations(
|
||||
PowerWatcher::ClassDerivations,
|
||||
"HUD"
|
||||
);
|
||||
|
||||
HUD::SharedData
|
||||
HUD::DefaultData(
|
||||
HUD::ClassDerivations,
|
||||
Subsystem::MessageHandlers,
|
||||
Subsystem::AttributeIndex,
|
||||
Subsystem::StateCount
|
||||
);
|
||||
|
||||
HUD::HUD(
|
||||
Mech *owner,
|
||||
int subsystem_ID,
|
||||
SubsystemResource *subsystem_resource,
|
||||
SharedData &shared_data
|
||||
):
|
||||
PowerWatcher(owner, subsystem_ID, subsystem_resource, shared_data)
|
||||
{
|
||||
Check(owner);
|
||||
Check_Pointer(subsystem_resource);
|
||||
|
||||
flickerRate = 0.0f;
|
||||
rotationOfTorsoHorizontal = 0.0f;
|
||||
torsoLimitRight = 0.0f;
|
||||
torsoLimitLeft = 0.0f;
|
||||
torsoSpeed = 0.0f;
|
||||
rangeToTarget = 0.0f;
|
||||
scratch1F0 = 0;
|
||||
visible = True;
|
||||
lockFlag = False;
|
||||
hotBoxVector = HudZeroVector;
|
||||
threatVector = HudZeroVector;
|
||||
compassHeading = 0.0f;
|
||||
blinkTimer = 0.0f;
|
||||
flickerTimer = 0.0f;
|
||||
blinkState = True;
|
||||
transitionFlag = False;
|
||||
previousHorizontal = 0.0f;
|
||||
|
||||
statusAlarm.Initialize(2);
|
||||
|
||||
freeAimSlew = 0.0f;
|
||||
scratch290 = 0.0f;
|
||||
horizontalTorsoOffset = 0.0f;
|
||||
|
||||
flickerRate = subsystem_resource->flickerRate;
|
||||
horizontalMovementPerSecond = subsystem_resource->horizontalMovementPerSecond;
|
||||
horizontalLimit = subsystem_resource->horizontalLimit;
|
||||
|
||||
//
|
||||
// The torso-orientation source is a Mech link not yet live at
|
||||
// construction time; HudSimulation refreshes these per-frame.
|
||||
//
|
||||
linkedEntity = 0;
|
||||
flickerActive = 0;
|
||||
|
||||
//
|
||||
// The master instance runs the HUD per-frame.
|
||||
//
|
||||
if (owner->GetInstance() != Entity::ReplicantInstance)
|
||||
{
|
||||
SetPerformance(&HUD::HudSimulation);
|
||||
}
|
||||
|
||||
Check_Fpu();
|
||||
}
|
||||
|
||||
HUD::~HUD()
|
||||
{
|
||||
}
|
||||
|
||||
Logical
|
||||
HUD::TestClass(Mech &)
|
||||
{
|
||||
return True;
|
||||
}
|
||||
|
||||
Logical
|
||||
HUD::TestInstance() const
|
||||
{
|
||||
return IsDerivedFrom(ClassDerivations);
|
||||
}
|
||||
|
||||
//
|
||||
//#############################################################################
|
||||
// @004b7830 -- the per-frame HUD, PARTIAL: the base tick, the heat/voltage
|
||||
// page gating and the blink are reconstructed (the donor's confident half);
|
||||
// the reticle / target-lock / torso-horizon math (its blocks 4-6) survives
|
||||
// only as summarised offsets in the recovered shard and stays STAGED --
|
||||
// the page visibility is the cockpit-bound behaviour, and it is real.
|
||||
//
|
||||
// The blink comparison is >= (the decomp shard's literal != would toggle
|
||||
// every tick through float inequality; the shard's own banner describes
|
||||
// "compared against flickerRate; on expiry ... toggles").
|
||||
//#############################################################################
|
||||
//
|
||||
void
|
||||
HUD::HudSimulation(Scalar time_slice)
|
||||
{
|
||||
Check(this);
|
||||
|
||||
PowerWatcher::Simulation(time_slice);
|
||||
|
||||
//
|
||||
// Page gating off our own mirrored alarms (the watchers, 5.3.109):
|
||||
// a heat failure or an unpowered watched subsystem hides the page.
|
||||
//
|
||||
Logical
|
||||
page_visible = True;
|
||||
|
||||
if (watchdogAlarm.GetLevel() != PoweredSubsystem::Ready)
|
||||
{
|
||||
page_visible = False;
|
||||
}
|
||||
if (heatAlarm.GetLevel() == HeatSink::FailureHeat)
|
||||
{
|
||||
page_visible = False;
|
||||
}
|
||||
|
||||
//
|
||||
// The blink.
|
||||
//
|
||||
if (transitionFlag == 0)
|
||||
{
|
||||
blinkTimer += time_slice;
|
||||
if (blinkTimer >= flickerRate)
|
||||
{
|
||||
blinkTimer = 0.0f;
|
||||
blinkState = (blinkState == 0);
|
||||
}
|
||||
}
|
||||
else if (transitionFlag == 1)
|
||||
{
|
||||
blinkState = True;
|
||||
}
|
||||
|
||||
visible = (page_visible && blinkState) ? 1 : 0;
|
||||
|
||||
//
|
||||
// STAGED (donor blocks 4-6): engine/start flag mirror, target lock +
|
||||
// sliding range + compass, and the torso-horizon slew -- summarised
|
||||
// offsets only in the recovered shard (part_013.c @004b7830).
|
||||
//
|
||||
|
||||
Check_Fpu();
|
||||
}
|
||||
|
||||
@@ -2042,7 +2042,12 @@ void
|
||||
|
||||
collision_damage.damageAmount = 0.0f;
|
||||
|
||||
if (getenv("BT_MECH_LOG"))
|
||||
static int s_crunchLog = -1;
|
||||
if (s_crunchLog < 0)
|
||||
{
|
||||
s_crunchLog = (getenv("BT_MECH_LOG") != NULL);
|
||||
}
|
||||
if (s_crunchLog)
|
||||
{
|
||||
DEBUG_STREAM << "[crash] CRUNCH (crushable) at ("
|
||||
<< post_snap_position.x << ","
|
||||
@@ -2083,7 +2088,20 @@ void
|
||||
ForceUpdate(0x20);
|
||||
}
|
||||
|
||||
if (getenv("BT_MECH_LOG"))
|
||||
//
|
||||
// Log the CONTACT EDGE only. This site once printed every
|
||||
// blocked frame -- 5487 lines in one wall-grinding run, each
|
||||
// formatted and pushed through the COM3 serial redirect, which
|
||||
// is what made the build feel vastly slower than the shipped
|
||||
// exe. The collisionState 0->1 edge is the same gate the audio
|
||||
// uses.
|
||||
//
|
||||
static int s_mechLog = -1;
|
||||
if (s_mechLog < 0)
|
||||
{
|
||||
s_mechLog = (getenv("BT_MECH_LOG") != NULL);
|
||||
}
|
||||
if (s_mechLog && collisionState.GetState() == 0)
|
||||
{
|
||||
DEBUG_STREAM << "[crash] BLOCK dmg="
|
||||
<< collision_damage.damageAmount
|
||||
@@ -2094,7 +2112,12 @@ void
|
||||
}
|
||||
}
|
||||
|
||||
if (getenv("BT_MECH_LOG"))
|
||||
static int s_simLog = -1;
|
||||
if (s_simLog < 0)
|
||||
{
|
||||
s_simLog = (getenv("BT_MECH_LOG") != NULL);
|
||||
}
|
||||
if (s_simLog)
|
||||
{
|
||||
static Scalar reportAccum = 0.0f;
|
||||
reportAccum += time_slice;
|
||||
|
||||
@@ -1,339 +1,357 @@
|
||||
//===========================================================================//
|
||||
// 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),
|
||||
ATTRIBUTE_ENTRY(MechControlsMapper, TargetRangeExponent,
|
||||
targetRangeExponent)
|
||||
};
|
||||
|
||||
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;
|
||||
targetRangeExponent = 0.0f; // staged: bound by an authored watcher
|
||||
lookState = LookNone;
|
||||
previousLookState = LookNone;
|
||||
{
|
||||
//
|
||||
// ELEMENTS(), never a literal -- this said 22 against reserved[21]
|
||||
// and wrote 4 bytes off the end of every mapper. Found by a sweep
|
||||
// after the same mistake surfaced in Torso (5.3.78).
|
||||
//
|
||||
int
|
||||
i;
|
||||
for (i = 0; i < (int)ELEMENTS(reserved); ++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);
|
||||
}
|
||||
const char *force_look = getenv("BT_FORCE_LOOK");
|
||||
if (force_look != NULL)
|
||||
{
|
||||
int state = atoi(force_look);
|
||||
lookLeft = (state == (int)LookLeftState) ? 1 : 0;
|
||||
lookRight = (state == (int)LookRightState) ? 1 : 0;
|
||||
lookBehind = (state == (int)LookBehindState) ? 1 : 0;
|
||||
lookDown = (state == (int)LookDownState) ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Look / eyepoint selection. Choose a look direction from the buttons;
|
||||
// when it changes, commit it -- the mech re-aims the eyepoint from its
|
||||
// authored look angles (and, once the weapon wave lands, re-arms the
|
||||
// per-view weapon fire enables through the same commit).
|
||||
//
|
||||
previousLookState = lookState;
|
||||
if (lookLeft > 0) lookState = LookLeftState;
|
||||
else if (lookRight > 0) lookState = LookRightState;
|
||||
else if (lookBehind > 0) lookState = LookBehindState;
|
||||
else if (lookDown > 0) lookState = LookDownState;
|
||||
else lookState = LookNone;
|
||||
|
||||
if (lookState != previousLookState)
|
||||
{
|
||||
mech->CommitLookState(lookState);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
//===========================================================================//
|
||||
// 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),
|
||||
ATTRIBUTE_ENTRY(MechControlsMapper, TargetRangeExponent,
|
||||
targetRangeExponent)
|
||||
};
|
||||
|
||||
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;
|
||||
targetRangeExponent = 0.0f; // staged: bound by an authored watcher
|
||||
lookState = LookNone;
|
||||
previousLookState = LookNone;
|
||||
{
|
||||
//
|
||||
// ELEMENTS(), never a literal -- this said 22 against reserved[21]
|
||||
// and wrote 4 bytes off the end of every mapper. Found by a sweep
|
||||
// after the same mistake surfaced in Torso (5.3.78).
|
||||
//
|
||||
int
|
||||
i;
|
||||
for (i = 0; i < (int)ELEMENTS(reserved); ++i)
|
||||
{
|
||||
reserved[i] = 0;
|
||||
}
|
||||
}
|
||||
//
|
||||
// Install the per-frame control-interpretation Performance.
|
||||
//
|
||||
SetPerformance(&MechControlsMapper::InterpretControls);
|
||||
|
||||
static int s_mpprLog = -1;
|
||||
if (s_mpprLog < 0) { s_mpprLog = (getenv("BT_MECH_LOG") != NULL); }
|
||||
if (s_mpprLog)
|
||||
{
|
||||
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. Parsed ONCE -- this block used to run four
|
||||
// getenv scans plus atof parses EVERY FRAME in the control path, part of
|
||||
// why the build lagged the shipped exe.
|
||||
//
|
||||
{
|
||||
static int s_forceParsed = 0;
|
||||
static int s_hasThrottle = 0, s_hasTurn = 0, s_hasElev = 0, s_hasLook = 0;
|
||||
static Scalar s_throttle = 0.0f, s_turn = 0.0f, s_elev = 0.0f;
|
||||
static int s_look = 0;
|
||||
if (!s_forceParsed)
|
||||
{
|
||||
s_forceParsed = 1;
|
||||
const char *e;
|
||||
e = getenv("BT_FORCE_THROTTLE");
|
||||
if (e != NULL) { s_hasThrottle = 1; s_throttle = (Scalar)atof(e); }
|
||||
e = getenv("BT_FORCE_TURN");
|
||||
if (e != NULL) { s_hasTurn = 1; s_turn = (Scalar)atof(e); }
|
||||
e = getenv("BT_FORCE_ELEV");
|
||||
if (e != NULL) { s_hasElev = 1; s_elev = (Scalar)atof(e); }
|
||||
e = getenv("BT_FORCE_LOOK");
|
||||
if (e != NULL) { s_hasLook = 1; s_look = atoi(e); }
|
||||
}
|
||||
if (s_hasThrottle)
|
||||
{
|
||||
throttlePosition = (s_throttle >= 0.0f) ? s_throttle : -s_throttle;
|
||||
reverseThrust = (s_throttle < 0.0f) ? 1 : 0;
|
||||
}
|
||||
if (s_hasTurn)
|
||||
{
|
||||
stickPosition.x = s_turn;
|
||||
}
|
||||
if (s_hasElev)
|
||||
{
|
||||
stickPosition.y = s_elev;
|
||||
}
|
||||
if (s_hasLook)
|
||||
{
|
||||
int state = s_look;
|
||||
lookLeft = (state == (int)LookLeftState) ? 1 : 0;
|
||||
lookRight = (state == (int)LookRightState) ? 1 : 0;
|
||||
lookBehind = (state == (int)LookBehindState) ? 1 : 0;
|
||||
lookDown = (state == (int)LookDownState) ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Look / eyepoint selection. Choose a look direction from the buttons;
|
||||
// when it changes, commit it -- the mech re-aims the eyepoint from its
|
||||
// authored look angles (and, once the weapon wave lands, re-arms the
|
||||
// per-view weapon fire enables through the same commit).
|
||||
//
|
||||
previousLookState = lookState;
|
||||
if (lookLeft > 0) lookState = LookLeftState;
|
||||
else if (lookRight > 0) lookState = LookRightState;
|
||||
else if (lookBehind > 0) lookState = LookBehindState;
|
||||
else if (lookDown > 0) lookState = LookDownState;
|
||||
else lookState = LookNone;
|
||||
|
||||
if (lookState != previousLookState)
|
||||
{
|
||||
mech->CommitLookState(lookState);
|
||||
}
|
||||
|
||||
static int s_mpprLog = -1;
|
||||
if (s_mpprLog < 0) { s_mpprLog = (getenv("BT_MECH_LOG") != NULL); }
|
||||
if (s_mpprLog)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user