diff --git a/emulator/render-bridge/pod_render_duck.conf b/emulator/render-bridge/pod_render_duck.conf new file mode 100644 index 00000000..ca19ddd1 --- /dev/null +++ b/emulator/render-bridge/pod_render_duck.conf @@ -0,0 +1,85 @@ +# pod_render_norio.conf -- pod_render_rec with the RIO serial port OFF. +# +# The emulator log shows a steady stream of serial1 RX OVERRUN errors on +# the RIO pipe, and controls post their events at HighEventPriority +# UNCONDITIONALLY (CONTROLS.HPP:250) -- so a chattering RIO port would +# flood a priority the background pump always serves first, starving the +# priority-0 renderer events the load gate waits on. This conf tests that +# by removing the port entirely. Everything else is identical to the rec +# conf, so a launch here and a hang there isolates the RIO. +# +[sdl] +output=opengl +# higher,higher not highest: HIGH_PRIORITY_CLASS starved the host desktop; +# with the retry patches a rare dropout self-recovers (see gauge_rio.conf). +priority=higher,higher +[dosbox] +memsize=32 +machine=svga_s3 +[cpu] +core=dynamic +cputype=pentium +cycles=max +[sblaster] +sbtype=sb16 +sbbase=220 +irq=5 +dma=1 +hdma=5 +[mixer] +# match the EMU8000s' native rate (no resample) and buffer ~60ms so brief +# emulation-thread stalls (RIO retry recovery) don't audibly chop +rate=44100 +blocksize=1024 +prebuffer=60 +[serial] +# RIO on COM1 with the low-latency options (rxpollus/rxburst) so the board's +# few-ms ACK deadline is met; plasma display on COM2 (real pod has both). +# VWE fork namedpipe backend (com0com/realport retired -- COM1/COM2 gone): +# DOSBox = pipe client (retry), vRIO/vPLASMA apps = servers; an unconnected +# pipe behaves as an unplugged cable so the mission still runs. serialnamedpipe.h +serial1=disabled +serial2=namedpipe pipe:vplasma +# live UNBUFFERED game output: DOS char devices are not buffered, so +# redirecting stdout to COM3 lands every line immediately. A normal +# '> file' redirect stays 0 bytes until the process exits, which hides +# all progress on a run that does NOT crash. +serial3=file file:C:\VWE\TeslaRel410\emulator\render-bridge\podlog.txt +[autoexec] +mount c "C:\VWE\TeslaRel410\ALPHA_1" +c: +cd \REL410\BT +set VIDEOFORMAT=svga +rem production pod card init (PARAMETR.BAT:181-186): DIAGNOSE + AWEUTIL per +rem card -- AWEUTIL /S does the EMU8000 bring-up and DRAM detect the HMI SOS +rem driver relies on; skipping it left the cards uninitialized (silent). +rem aweutil /s SKIPPED for now: it verifies the AWE32 GM ROM, which the +rem emulated cards lack (hangs in a retry loop) -- restore once the ROM is +rem dumped from a real card. diagnose /s kept (passes, sets mixer config). +set BLASTER=A220 I5 D1 H5 P330 T6 +c:\sb16\diagnose /s +set BLASTER=A240 I7 D3 H6 P300 T6 +c:\sb16\diagnose /s +set BLASTER=A220 I5 D1 H5 P330 T6 +set TEMP=c:\ +rem arena1 city mission (TESTARN.EGG: map=arena1, time=day) with the RIO +rem attached; stdout redirected so mission-load progress survives kills. +set BT_JOINTS=1 +set L4VIEWEXT=1 +set BT_STACK_LOG=1 +set BT_MECH_LOG=1 +set BT_FORCE_DUCK=1 +set BT_GAUGE_LOG=1 +set BT_FORCE_THROTTLE=0 +set BT_FORCE_TURN=0 +set HEAPSIZE=15000000 +set L4GAUGE=640x480x16 +call setenv.bat r s n p +32rtm.exe -x +BTL4REC.EXE -egg testarn.egg > COM3 + +echo GAME-RC=%errorlevel% >> RC.TXT +32rtm.exe -u +echo ALPHA1-RUN-DONE +pause + diff --git a/restoration/source410/BT/MECH.CPP b/restoration/source410/BT/MECH.CPP index bf14fe63..150d462c 100644 --- a/restoration/source410/BT/MECH.CPP +++ b/restoration/source410/BT/MECH.CPP @@ -86,7 +86,8 @@ Derivation const Receiver::HandlerEntry Mech::MessageHandlerEntries[] = { - MESSAGE_ENTRY(Mech, TakeDamage) + MESSAGE_ENTRY(Mech, TakeDamage), + MESSAGE_ENTRY(Mech, DuckRequest) }; Mech::MessageHandlerSet @@ -292,6 +293,25 @@ Mech::Mech( radarLinearPosition = &localOrigin.linearPosition; radarAngularPosition = &localOrigin.angularPosition; duckState = 0; + + // + // The duck system's rest state (binary ctor tail @004a1674: the volume + // alarm at +0x4c4 initialises to level 1 = STANDING, and the two + // cylinder heights capture template maxY and 0.6 x it -- the heights + // are seeded after the collision template resolves, below). + // + duckRequestLatch = 0; + duckPhaseRequest = 0; + duckDemand = 0.0f; + collisionVolumeState.Initialize(2); + collisionVolumeState.SetLevel(1); + standingVolumeHeight = 0.0f; + duckedVolumeHeight = 0.0f; + if (collisionTemplate != NULL) + { + standingVolumeHeight = collisionTemplate->maxY; + duckedVolumeHeight = 0.6f * standingVolumeHeight; + } unstablePercentage = 0.0f; // staged: no instability model yet collisionSpeed = 0.0f; // staged: audio watcher set (see MECH.HPP) distanceToMissile = 0.0f; @@ -810,6 +830,50 @@ Logical vehicle->GetSimulationState() == 9; } +// +//############################################################################# +// DuckRequestMessageHandler -- binary @0049fa00 (authentic name "DuckRequest" +// from the handler table @0x10bc7c). A positive request latches one duck +// evaluation for the next master frame; the frame consumes the latch either +// way. +//############################################################################# +// +void + Mech::DuckRequestMessageHandler(Receiver::Message *message) +{ + Check(this); + Check_Pointer(message); + + if (((int *)(message + 1))[0] > 0) + { + duckRequestLatch = 1; + } +} + +// +//############################################################################# +// The collision-volume swap (binaries @004ac04c / @004ac064): write the +// template's maxY -- the box TOP; the ground probe's minY is untouched. +// Level 1 (standing) restores the captured height; level 0 (ducked) the +// 0.6x height. +//############################################################################# +// +void + Mech::SetStandingCollisionVolume() +{ + Check(this); + Check(collisionTemplate); + collisionTemplate->maxY = standingVolumeHeight; +} + +void + Mech::SetDuckedCollisionVolume() +{ + Check(this); + Check(collisionTemplate); + collisionTemplate->maxY = duckedVolumeHeight; +} + // //############################################################################# // TakeDamageMessageHandler -- the Mech override of the Entity damage entry @@ -1177,6 +1241,12 @@ void void Mech::Reset(const Origin &origin, Logical full_reset) { + // + // The binary Reset (@0049fb74) drops any pending duck latch with the + // rest of the per-life state. + // + duckRequestLatch = 0; + Check(this); localOrigin = origin; @@ -1619,6 +1689,44 @@ void // legResetLatch = 0; + // + // BT_FORCE_DUCK=1: one authentic duck press every ~4s on its own + // clock (independent of the one-shot button harness below) -- the + // DuckRequest message, plus the held cell and analog demand every + // frame. Press one ducks, the next (from the hold) stands: the + // full-machine cycle soak. + // + { + static int s_forceDuck = -1; + static Scalar s_duckClock = 0.0f; + if (s_forceDuck < 0) + { + s_forceDuck = (getenv("BT_FORCE_DUCK") != NULL) ? 1 : 0; + } + if (s_forceDuck) + { + MechControlsMapper + *pulse_mapper = (MechControlsMapper *)subsystemArray[0]; + if (pulse_mapper != NULL) + { + pulse_mapper->duckCommand = 1; + } + duckDemand = 1.0f; + s_duckClock += time_slice; + if (s_duckClock >= 4.0f) + { + s_duckClock = 0.0f; + ReceiverDataMessageOf + duck_press( + Mech::DuckRequestMessageID, + sizeof(ReceiverDataMessageOf), + 1); + Dispatch(&duck_press); + } + } + } + + if (limping) { AdvanceLegAnimationGimp(time_slice); @@ -1641,6 +1749,150 @@ void ((Gyroscope *)gyroSubsystem)->WriteMechJoint(); } + // + // THE DUCK DRIVER (master perf @004a9f61..@004aa155, disasm-decoded). + // + // Phase pick, every frame: not standing (raw state != 0) or duck button + // released -> no request. Standing with the button held: from gait + // state 0/4 (stand / turn-in-place), request duck-DOWN while the analog + // demand clears the 1e-4 deadzone; from gait state 1 (ducked hold), + // request duck-UP; anything else, none. + // + // The arms fire only on a DuckRequest-latched frame of a squat-capable + // mech, bind the LEG channel only (squat is a leg figure), and set the + // volume alarm: down -> level 0 (ducked), up -> level 1 (standing). + // The latch is consumed unconditionally. A mech that STARTS LIMPING + // while ducked is stood up by force (modes 3/4 with gait state 1). + // On a volume-alarm edge the collision template's top swaps between + // the captured heights; any other level is the authentic + // "Whoa! Bad Collision Volume State!" Fail. The dirty marks (binary + // MarkUpdate(8)+MarkUpdate(1) per arm) ride ForceUpdate -- still a + // no-op here until the update-record feed lands. + // + { + int + raw_mode = (int)GetSimulationState(); + // + // The mapper lives in roster slot 0 (the binary caches it at + // mech+0x190; our tree resolves the same object through the roster). + // + MechControlsMapper + *duck_mapper = (MechControlsMapper *)subsystemArray[0]; + int + duck_held = + (duck_mapper != NULL && duck_mapper->DuckCommand() != 0); + + if (raw_mode != 0 || !duck_held) + { + duckPhaseRequest = 0; + } + else + { + int + leg_state = (int)legStateAlarm.GetLevel(); + if (leg_state == 0 || leg_state == 4) + { + duckPhaseRequest = + (duckDemand <= 0.0001f && duckDemand >= -0.0001f) ? 0 : 1; + } + else if (leg_state == 1) + { + duckPhaseRequest = 2; + } + else + { + duckPhaseRequest = 0; + } + } + + { + static int s_duckEvalLog = -1; + if (s_duckEvalLog < 0) + { + s_duckEvalLog = (getenv("BT_MECH_LOG") != NULL); + } + if (s_duckEvalLog && duckRequestLatch != 0) + { + DEBUG_STREAM << "[duck] eval mode=" << raw_mode + << " held=" << duck_held + << " leg=" << legStateAlarm.GetLevel() + << " phase=" << duckPhaseRequest + << " capable=" << squatCapable << endl; + } + } + if (duckRequestLatch != 0 && squatCapable != 0) + { + if (duckPhaseRequest == 1) + { + SetLegAnimation(2); // the sqd (squat-down) clip slot -- the + // SLOT map diverges from the enum's walk + // names here (MECH2.NOTES.md) + ForceUpdate(8); + ForceUpdate(1); + collisionVolumeState.SetLevel(0); + } + else if (duckPhaseRequest == 2) + { + SetLegAnimation(3); // the squ (squat-up) clip slot + ForceUpdate(8); + ForceUpdate(1); + collisionVolumeState.SetLevel(1); + } + } + duckRequestLatch = 0; + + // + // Limping stands you up (modes 3/4 with the legs still in the + // ducked hold). + // + if ( + (raw_mode == 3 || raw_mode == 4) && + (int)legStateAlarm.GetLevel() == 1 + ) + { + SetLegAnimation(3); // squ -- forced stand-up + ForceUpdate(8); + ForceUpdate(1); + collisionVolumeState.SetLevel(1); + } + + // + // The volume swap on an alarm edge, and the DuckState publish. + // + if (collisionVolumeState.GetState() != collisionVolumeState.GetOldState()) + { + static int s_duckLog = -1; + if (s_duckLog < 0) + { + s_duckLog = (getenv("BT_MECH_LOG") != NULL); + } + if (s_duckLog) + { + DEBUG_STREAM << "[duck] volume level " + << collisionVolumeState.GetOldState() << " -> " + << collisionVolumeState.GetState() + << " legState=" << legStateAlarm.GetLevel() + << " maxY " << standingVolumeHeight + << "/" << duckedVolumeHeight << endl; + } + switch (collisionVolumeState.GetLevel()) + { + case 1: + SetStandingCollisionVolume(); + duckState = 0; + break; + case 0: + SetDuckedCollisionVolume(); + duckState = 1; + break; + default: + Fail("Whoa! Bad Collision Volume State!\n"); + break; + } + } + collisionVolumeState.SetLevel(collisionVolumeState.GetLevel()); + } + // // THE TURN-IN-PLACE DISPATCHER (master perf @0x4aa505-0x4aa588, decoded // from raw disasm -- Ghidra never decompiled the function). From diff --git a/restoration/source410/BT/MECH.HPP b/restoration/source410/BT/MECH.HPP index c20c34f7..da129c52 100644 --- a/restoration/source410/BT/MECH.HPP +++ b/restoration/source410/BT/MECH.HPP @@ -221,6 +221,23 @@ class Mech: public JointedMover { + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Message Support -- the authentic roster, read from the binary's own + // handler table (ids and NAME STRINGS live at 0x10bbf0../0x50c146..): + // 0x12 TakeDamage (Entity overlay), 0x14 PlayerLink (Entity overlay), + // then the mech's own block from Entity::NextMessageID. + // + public: + enum { + RealMaxSpeedMessageID = Entity::NextMessageID, // 0x15 @0049f604 + BalanceCoolantMessageID, // 0x16 @0049f728 + SetBurningStateMessageID, // 0x17 @0049f674 + ClearBurningStateMessageID, // 0x18 @0049f700 + EjectPilotMessageID, // 0x19 @0049f854 + DuckRequestMessageID, // 0x1A @0049fa00 + NextMessageID + }; + // // The per-zone damage code walks the roster / segment table / alarms // directly (the 1995 arrangement, mirrored by the reconstruction). @@ -885,6 +902,41 @@ Point3D *radarLinearPosition; Quaternion *radarAngularPosition; int duckState; + + // + // THE DUCK SYSTEM (master perf @004a9f61..@004aa155, disasm-decoded). + // + // duckRequestLatch (+0x398) ONE-SHOT: set by the DuckRequest + // message handler (@0049fa00, request > 0), consumed -- and + // CLEARED -- every master frame after the arm check; also + // cleared by Reset. Only a latched frame may bind squat clips. + // duckPhaseRequest (+0x3f8) 0 none / 1 duck-down / 2 duck-up, + // recomputed every frame from the raw simulation state, the + // mapper's duck button cell, the leg gait state and the duck + // analog demand (deadzone 1e-4 @0x4ab16c). + // duckDemand (+0x79c) the analog crouch input. + // collisionVolumeState (+0x4c4) the volume alarm: level 1 = + // STANDING, 0 = DUCKED. On an edge the collision template's + // maxY (BoxedSolid +0xc -- the box TOP; the ground probe's + // minY is untouched) swaps between the two ctor-captured + // heights; any other level Fails ("Whoa! Bad Collision + // Volume State!", MECH4.CPP line 417). + // standing/duckedVolumeHeight (+0x518/+0x51c) captured at ctor + // time: template maxY, and 0.6 x it (the double @004a2d38). + // + int duckRequestLatch; + int duckPhaseRequest; + Scalar duckDemand; + AlarmIndicator collisionVolumeState; + Scalar standingVolumeHeight; + Scalar duckedVolumeHeight; + + void + DuckRequestMessageHandler(Receiver::Message *message); + void + SetStandingCollisionVolume(); // binary @004ac04c... see .CPP + void + SetDuckedCollisionVolume(); // // STAGED VALUE (the instability model is not reconstructed). // The mech streams the authored unstable* effect constants in its diff --git a/restoration/source410/BT/MECH2.CPP b/restoration/source410/BT/MECH2.CPP index 91e9b5ff..13c00c5a 100644 --- a/restoration/source410/BT/MECH2.CPP +++ b/restoration/source410/BT/MECH2.CPP @@ -66,6 +66,11 @@ void Mech::SetLegAnimation(int state) { + if ((state == 2 || state == 3) && getenv("BT_MECH_LOG")) + { + DEBUG_STREAM << "[duck] arm slot " << state + << " clip=" << animationClips[state] << endl; + } Check(this); // // Bounded by the SLOT count, not the name count: slot 0x20 is the @@ -192,6 +197,10 @@ Scalar return 0.0f; case 2: + if (getenv("BT_MECH_LOG")) + { + DEBUG_STREAM << "[duck] sqd finished -> ducked hold" << endl; + } mech->legStateAlarm.SetLevel(1); return 0.0f; diff --git a/restoration/source410/BT/MECH4.NOTES.md b/restoration/source410/BT/MECH4.NOTES.md index 1bf90b4d..d88d1114 100644 --- a/restoration/source410/BT/MECH4.NOTES.md +++ b/restoration/source410/BT/MECH4.NOTES.md @@ -86,8 +86,24 @@ per contact by `Mover::ProcessCollisionList`. The decomp matching our Simulate's structure and locating every staged gap: 1. controls/demand intake (mech.cpp helpers 0x4a4eab/0x4a4ee9/0x4a4f9e) 2. three cerr+SetLevel triplets — illegal-state alarm guards - 3. **THE DUCK DRIVER**: three arms of {mech2 SetAnim 0x4a7fc4, - MarkUpdate ×2, AlarmIndicator::SetLevel} — duck down/hold/up + 3. **THE DUCK DRIVER** — DECODED + LANDED 5.3.120, live-verified + (BT_FORCE_DUCK cycles down/hold/up continuously on the rig). The + full system: DuckRequest message 0x1A (authentic name from the + binary's handler table; roster = RealMaxSpeed 0x15, BalanceCoolant + 0x16, SetBurningState 0x17, ClearBurningState 0x18, EjectPilot 0x19, + DuckRequest 0x1A) latches one evaluation (+0x398, one-shot, Reset + clears); the phase machine reads the RAW simulation state, the + mapper's duck cell (+0x25c) and the analog demand (+0x79c, deadzone + 1e-4 @0x4ab16c): gait 0/4 + input -> down (LEG slot 2, sqd), gait 1 + -> up (slot 3, squ); limp modes 3/4 force stand-up; the + collisionVolumeState alarm (+0x4c4; 1=standing, 0=ducked; "Whoa! Bad + Collision Volume State!" guard, line 417) swaps the collision + template's maxY (ExtentBox +0xc == the box TOP; the ground probe's + minY untouched) between the ctor-captured height and 0.6x it (the + double @004a2d38); duckState publishes for the cockpit selector. + Still open here: the two MarkUpdate masks (8 then 1) ride our + ForceUpdate no-op, and the mapper duck cell's streamed binding id + awaits a BT_MAP_LOG audit on a duck-bound RES. 4. verify-guarded state read (mech4 accessors + Fail 0x40385c) 5. gyro feed a (vec pair → gyro_b 0x4b2de4, gyro_a 0x4b2d8c) 6. death arm: InDeathTransition 0x49fb54 → the 1.9KB mech2 advancer diff --git a/restoration/source410/BT/MECHMPPR.CPP b/restoration/source410/BT/MECHMPPR.CPP index d67e217d..59db8ef4 100644 --- a/restoration/source410/BT/MECHMPPR.CPP +++ b/restoration/source410/BT/MECHMPPR.CPP @@ -95,6 +95,7 @@ MechControlsMapper::MechControlsMapper( shared_data ) { + duckCommand = 0; Check(owner); // // Prime the published control inputs to neutral. Per-frame interpretation diff --git a/restoration/source410/BT/MECHMPPR.HPP b/restoration/source410/BT/MECHMPPR.HPP index d9e26245..ab7d1311 100644 --- a/restoration/source410/BT/MECHMPPR.HPP +++ b/restoration/source410/BT/MECHMPPR.HPP @@ -1,210 +1,223 @@ -//===========================================================================// -// File: mechmppr.hpp // -// Project: BattleTech // -// Contents: Implementation details for the mech controls mapper // -//---------------------------------------------------------------------------// -// Date Who Modification // -// -------- --- ---------------------------------------------------------- // -// // -//---------------------------------------------------------------------------// -// Copyright (C) 1995, Virtual World Entertainment, Inc. // -// All Rights reserved worldwide // -// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL // -//===========================================================================// - -#if !defined(MECHMPPR_HPP) -# define MECHMPPR_HPP - -# if !defined(SUBSYSTM_HPP) -# include -# endif - -# if !defined(CSTR_HPP) -# include -# endif - -# if !defined(CONTROLS_HPP) -# include -# endif - -# if !defined(VECTOR2D_HPP) -# include -# endif - -//##################### Forward Class Declarations ####################### - class Mech; - -//########################################################################### -//######################## MechControlsMapper ########################### -//########################################################################### - - class MechControlsMapper: - public Subsystem - { - //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - // Shared Data Support - // - public: - static Derivation ClassDerivations; - static SharedData DefaultData; - - static const IndexEntry AttributePointers[]; - static AttributeIndexSet AttributeIndex; - - //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - // Attribute IDs -- the control inputs the streamed mappings bind to. - // The chain ends at Subsystem::NextAttributeID == 2. - // - public: - enum { - MechControlsMapperPadFirstAttributeID = 2, - StickPositionAttributeID, // 3 - ThrottlePositionAttributeID, // 4 - PedalsPositionAttributeID, // 5 - ReverseThrustAttributeID, // 6 - SpeedDemandAttributeID, // 7 - TurnDemandAttributeID, // 8 - LookForwardAttributeID, // 9 - LookLeftAttributeID, // a - LookRightAttributeID, // b - LookBehindAttributeID, // c - LookDownAttributeID, // d - TorsoUpAttributeID, // e - TorsoDownAttributeID, // f - TorsoLeftAttributeID, // 10 - TorsoRightAttributeID, // 11 - TorsoCenterAttributeID, // 12 - ControlModeAttributeID, // 13 - DisplayModeAttributeID, // 14 - PilotArrayPageAttributeID, // 15 - PilotArrayAttributeID, // 16 - TargetRangeExponentAttributeID, // authored watcher - NextAttributeID - }; - - //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - // Control modes - // - public: - enum ControlMode { - BasicMode = 0, - StandardMode, - VeteranMode - }; - - //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - // Look states. A five-state machine off the pod's look buttons; on a state - // change InterpretControls calls Mech::CommitLookState, which re-aims the - // eyepoint from the model's authored look angles (and, once the weapon wave - // lands, re-arms the per-view weapon fire enables). - // - public: - enum LookState { - LookNone = 0, - LookLeftState, - LookRightState, - LookBehindState, - LookDownState - }; - - //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - // Construction and Destruction - // - public: - MechControlsMapper( - Mech *owner, - int subsystem_ID, - CString subsystem_name, - RegisteredClass::ClassID class_ID, - SharedData &shared_data = DefaultData - ); - - ~MechControlsMapper(); - - Logical - TestInstance() const; - - //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - // Per-frame control interpretation (the mapper's Performance): reads the - // engine-pushed raw input attributes (throttle / stick / pedals / buttons) - // and publishes the locomotion + aim demands the mech drive consumes. - // - public: - typedef void - (MechControlsMapper::*Performance)(Scalar time_slice); - void - SetPerformance(Performance performance) - { - Check(this); - activePerformance = (Simulation::Performance)performance; - } - - void - InterpretControls(Scalar time_slice); - - Mech* - GetMech() { Check(this); return (Mech *)GetEntity(); } - Scalar - GetSpeedDemand() const { Check(this); return speedDemand; } - Scalar - GetTurnDemand() const { Check(this); return turnDemand; } - - // - // The LIMP machines write the demand cell DOWN to the damaged side's - // speed cap (binary GimpLegClipFinished @004a7970 preamble writes - // mapper+0x128 directly) -- a limping mech cannot command more speed - // than its limp figure carries. - // - void - SetSpeedDemand(Scalar demand) - { Check(this); speedDemand = demand; } - - //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - // Local Data -- the published control inputs (the streamed mappings write - // these; InterpretControls, phase 5.3, reads them to drive the mech). - // - public: - // - // The POD ROSTER (binary mapper attributes PilotArrayPage/PilotArray, - // ids 15/16): the comm scoreboard's row source. Zero = no roster - // authored (a solo mission), which is why the shipped cockpit leaves - // that page blank. - // - int - GetPilotArray() const { Check(this); return pilotArray; } - int - GetPilotArrayPage() const { Check(this); return pilotArrayPage; } - - - protected: - ControlsJoystick stickPosition; - Scalar throttlePosition; - Scalar pedalsPosition; - ControlsButton reverseThrust; - Scalar speedDemand; - Scalar turnDemand; - ControlsButton lookForward; - ControlsButton lookLeft; - ControlsButton lookRight; - ControlsButton lookBehind; - ControlsButton lookDown; - ControlsButton torsoUp; - ControlsButton torsoDown; - ControlsButton torsoLeft; - ControlsButton torsoRight; - ControlsButton torsoCenter; - int controlMode; - int displayMode; - int pilotArrayPage; - int pilotArray; - int lookState; - int previousLookState; - // - // Takes one reserved word (Scalar and int are both 4 bytes here), so - // the class size and every existing offset stay exactly as they were. - // - Scalar targetRangeExponent; // authored watcher - int reserved[21]; - }; - -#endif +//===========================================================================// +// File: mechmppr.hpp // +// Project: BattleTech // +// Contents: Implementation details for the mech controls mapper // +//---------------------------------------------------------------------------// +// Date Who Modification // +// -------- --- ---------------------------------------------------------- // +// // +//---------------------------------------------------------------------------// +// Copyright (C) 1995, Virtual World Entertainment, Inc. // +// All Rights reserved worldwide // +// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL // +//===========================================================================// + +#if !defined(MECHMPPR_HPP) +# define MECHMPPR_HPP + +# if !defined(SUBSYSTM_HPP) +# include +# endif + +# if !defined(CSTR_HPP) +# include +# endif + +# if !defined(CONTROLS_HPP) +# include +# endif + +# if !defined(VECTOR2D_HPP) +# include +# endif + +//##################### Forward Class Declarations ####################### + class Mech; + +//########################################################################### +//######################## MechControlsMapper ########################### +//########################################################################### + + class MechControlsMapper: + public Subsystem + { + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // The duck button cell (binary mapper+0x25c) -- a device-poll write + // destination like the torso command cells; the master perf's duck + // driver reads it as the held-button gate. The streamed attribute + // binding id is still to be audited (BT_MAP_LOG) -- until a RES table + // binds it, BT_FORCE_DUCK drives it. + // + public: + int + DuckCommand() + { Check(this); return duckCommand; } + int duckCommand; + + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Shared Data Support + // + public: + static Derivation ClassDerivations; + static SharedData DefaultData; + + static const IndexEntry AttributePointers[]; + static AttributeIndexSet AttributeIndex; + + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Attribute IDs -- the control inputs the streamed mappings bind to. + // The chain ends at Subsystem::NextAttributeID == 2. + // + public: + enum { + MechControlsMapperPadFirstAttributeID = 2, + StickPositionAttributeID, // 3 + ThrottlePositionAttributeID, // 4 + PedalsPositionAttributeID, // 5 + ReverseThrustAttributeID, // 6 + SpeedDemandAttributeID, // 7 + TurnDemandAttributeID, // 8 + LookForwardAttributeID, // 9 + LookLeftAttributeID, // a + LookRightAttributeID, // b + LookBehindAttributeID, // c + LookDownAttributeID, // d + TorsoUpAttributeID, // e + TorsoDownAttributeID, // f + TorsoLeftAttributeID, // 10 + TorsoRightAttributeID, // 11 + TorsoCenterAttributeID, // 12 + ControlModeAttributeID, // 13 + DisplayModeAttributeID, // 14 + PilotArrayPageAttributeID, // 15 + PilotArrayAttributeID, // 16 + TargetRangeExponentAttributeID, // authored watcher + NextAttributeID + }; + + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Control modes + // + public: + enum ControlMode { + BasicMode = 0, + StandardMode, + VeteranMode + }; + + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Look states. A five-state machine off the pod's look buttons; on a state + // change InterpretControls calls Mech::CommitLookState, which re-aims the + // eyepoint from the model's authored look angles (and, once the weapon wave + // lands, re-arms the per-view weapon fire enables). + // + public: + enum LookState { + LookNone = 0, + LookLeftState, + LookRightState, + LookBehindState, + LookDownState + }; + + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Construction and Destruction + // + public: + MechControlsMapper( + Mech *owner, + int subsystem_ID, + CString subsystem_name, + RegisteredClass::ClassID class_ID, + SharedData &shared_data = DefaultData + ); + + ~MechControlsMapper(); + + Logical + TestInstance() const; + + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Per-frame control interpretation (the mapper's Performance): reads the + // engine-pushed raw input attributes (throttle / stick / pedals / buttons) + // and publishes the locomotion + aim demands the mech drive consumes. + // + public: + typedef void + (MechControlsMapper::*Performance)(Scalar time_slice); + void + SetPerformance(Performance performance) + { + Check(this); + activePerformance = (Simulation::Performance)performance; + } + + void + InterpretControls(Scalar time_slice); + + Mech* + GetMech() { Check(this); return (Mech *)GetEntity(); } + Scalar + GetSpeedDemand() const { Check(this); return speedDemand; } + Scalar + GetTurnDemand() const { Check(this); return turnDemand; } + + // + // The LIMP machines write the demand cell DOWN to the damaged side's + // speed cap (binary GimpLegClipFinished @004a7970 preamble writes + // mapper+0x128 directly) -- a limping mech cannot command more speed + // than its limp figure carries. + // + void + SetSpeedDemand(Scalar demand) + { Check(this); speedDemand = demand; } + + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Local Data -- the published control inputs (the streamed mappings write + // these; InterpretControls, phase 5.3, reads them to drive the mech). + // + public: + // + // The POD ROSTER (binary mapper attributes PilotArrayPage/PilotArray, + // ids 15/16): the comm scoreboard's row source. Zero = no roster + // authored (a solo mission), which is why the shipped cockpit leaves + // that page blank. + // + int + GetPilotArray() const { Check(this); return pilotArray; } + int + GetPilotArrayPage() const { Check(this); return pilotArrayPage; } + + + protected: + ControlsJoystick stickPosition; + Scalar throttlePosition; + Scalar pedalsPosition; + ControlsButton reverseThrust; + Scalar speedDemand; + Scalar turnDemand; + ControlsButton lookForward; + ControlsButton lookLeft; + ControlsButton lookRight; + ControlsButton lookBehind; + ControlsButton lookDown; + ControlsButton torsoUp; + ControlsButton torsoDown; + ControlsButton torsoLeft; + ControlsButton torsoRight; + ControlsButton torsoCenter; + int controlMode; + int displayMode; + int pilotArrayPage; + int pilotArray; + int lookState; + int previousLookState; + // + // Takes one reserved word (Scalar and int are both 4 bytes here), so + // the class size and every existing offset stay exactly as they were. + // + Scalar targetRangeExponent; // authored watcher + int reserved[21]; + }; + +#endif