//===========================================================================// // 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) }; 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; lookState = LookNone; previousLookState = LookNone; { for (int i = 0; i < 22; ++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); }