diff --git a/restoration/source410/BT/HUD.CPP b/restoration/source410/BT/HUD.CPP index bde15319..8c427094 100644 --- a/restoration/source410/BT/HUD.CPP +++ b/restoration/source410/BT/HUD.CPP @@ -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 -#pragma hdrstop - -#if !defined(HUD_HPP) -# include -#endif - -#if !defined(MECH_HPP) -# include -#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 +#pragma hdrstop + +#if !defined(HUD_HPP) +# include +#endif + +#if !defined(MECH_HPP) +# include +#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(); +} diff --git a/restoration/source410/BT/MECH.CPP b/restoration/source410/BT/MECH.CPP index 49d6f335..1a011fd8 100644 --- a/restoration/source410/BT/MECH.CPP +++ b/restoration/source410/BT/MECH.CPP @@ -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; diff --git a/restoration/source410/BT/MECHMPPR.CPP b/restoration/source410/BT/MECHMPPR.CPP index 65972010..d67e217d 100644 --- a/restoration/source410/BT/MECHMPPR.CPP +++ b/restoration/source410/BT/MECHMPPR.CPP @@ -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 -#pragma hdrstop - -#if !defined(MECHMPPR_HPP) -# include -#endif - -#if !defined(MECH_HPP) -# include -#endif - -#if !defined(TORSO_HPP) -# include -#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 +#pragma hdrstop + +#if !defined(MECHMPPR_HPP) +# include +#endif + +#if !defined(MECH_HPP) +# include +#endif + +#if !defined(TORSO_HPP) +# include +#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); +}