Files
CydandClaude Fable 5 fe97746a51 Input: PadRIO -- play without the pod (L4CONTROLS=PAD), Workstream A.1
Ported from RP412: RIOBase split out of the serial RIO (L4RIO.h),
rioPointer is RIOBase* (L4CTRL.h), PAD token -> new PadRIO() speaking
the RIO surface from an XInput pad + keyboard (L4PADRIO/L4PADBINDINGS,
vRIO bindings.txt grammar, hot-plug), KeyLight RGB mirror TU
(BT412KEYLIGHT, /std:c++17 per-file).

BT-side fixes PadRIO forced into the open:
- Both keyboard input bridges (mech4.cpp, mechmppr.cpp BT_KEY_BRIDGE)
  stand down when a RIO device exists -- they overwrote the engine
  controls push every frame. M/X conveniences stay live.
- Mapper attribute chain OFF BY ONE (latent real-pod bug): the DOS
  chain below MechControlsMapper carried two base attributes, WinTesla
  carries one, and AttributeIndexSet::Find is positional -- the .CTL
  stick mapping wrote throttlePosition. Pad slot + binary-locked enum;
  gotcha ledgered (reconstruction-gotchas #11).

Verified: PAD throttle lever ramps + sticks, stick turns with the
authentic speed-vs-turn clamp (61.5 -> 22.0 u/s), mech drives; keyboard
fallback intact (BT_FORCE_THROTTLE harness). New diags: BT_CTRLMAP_LOG,
BT_STICK_LOG. (Phase 2 of docs/BT412-ROADMAP.md)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-14 08:16:33 -05:00

669 lines
15 KiB
C++

#pragma once
#include "..\munga\controls.h"
#include "l4rio.h"
#include "l4keybd.h"
#include "l4mouse.h"
#include "l4joystk.h"
#include "l4lamp.h"
#include "..\munga\time.h"
#include "..\munga\entity.h"
#include <dinput.h>
class LBE4ControlsManager;
class L4MappableButtonManager;
//########################################################################
//############################ Joystick classes ##########################
//########################################################################
class Joystick;
class OrdinaryJoystick;
class FlightStickPro;
class ThrustMaster;
class Joystick SIGNATURED
{
friend class LBE4ControlsManager;
friend class OrdinaryJoystick;
friend class FlightStickPro;
friend class ThrustMaster;
protected:
Joystick();
public:
virtual ~Joystick();
virtual void
BeginAlignment();
virtual void
EndAlignment();
virtual void
Update();
void
SetDeadBand(Scalar amount_of_deadband);
Logical
TestInstance() const;
protected:
virtual void
EndAndSaveAlignment(NotationFile *init_file);
public:
enum ButtonState {
NoButton = 0,
Trigger = 0x01,
Thumb1 = 0x02,
Thumb2 = 0x04,
Thumb3 = 0x08,
HatUp = 0x10,
HatDown = 0x20,
HatLeft = 0x40,
HatRight = 0x80,
Throttle = 0x100
};
Vector2DOf<Scalar> Value;
Vector2DOf<Scalar> ExtendedValue;
Vector2DOf<Scalar> Rotation;
int ButtonReleased;
int ButtonPressed;
protected:
FilterChannel *channel[2];
int oldButtons;
};
class OrdinaryJoystick:
public Joystick
{
public:
OrdinaryJoystick();
~OrdinaryJoystick();
void Update();
};
class FlightStickPro:
public Joystick
{
public:
FlightStickPro();
~FlightStickPro();
void Update();
void
BeginAlignment();
void
EndAlignment();
protected:
FilterChannel
*throttle;
};
class ThrustMaster:
public Joystick
{
friend BOOL CALLBACK EnumJoysticksCallback(const DIDEVICEINSTANCE* pdidInstance, VOID* pContext);
friend BOOL CALLBACK EnumAxesCallback(LPCDIDEVICEOBJECTINSTANCE lpddoi, LPVOID pvRef);
public:
ThrustMaster(HINSTANCE hInstance, HWND hWnd);
~ThrustMaster();
void Update();
int hatMax, prevHatState;
private:
bool Poll();
LPDIRECTINPUT8 mDI;
LPDIRECTINPUTDEVICE8 mJoystick;
DIJOYSTATE2 mJoystate;
LONG xAxisMin, xAxisMax;
LONG yAxisMin, yAxisMax;
LONG zAxisMin, zAxisMax;
LONG rzAxisMin, rzAxisMax;
};
enum RioStreamEventType
{
analog,
digital
};
struct RIOStreamEvent
{
Time timeStamp;
RioStreamEventType eventType;
union
{
struct
{
Scalar
joystickX,
joystickY,
throttle,
pedalLeft,
pedalRight;
}
analog;
RIO::RIOEvent rioEvent;
}
data;
};
//#########################################################################
//########################## LBE4ControlsManager ##########################
//#########################################################################
class LBE4ControlsManager:
public ControlsManager
{
friend class L4MappableButtonManager; // allows access to 'groupEnable'
//-------------------------------------------------------------------
// Shared data support
//-------------------------------------------------------------------
public:
static Derivation *GetClassDerivations();
static SharedData DefaultData;
//-------------------------------------------------------------------
// Message support
//-------------------------------------------------------------------
// public:
// enum {
// BeginCalibrationMessageID=ControlsManager::NextMessageID,
// EndCalibrationMessageID,
// NextMessageID
// };
// private:
// static const HandlerEntry MessageHandlerEntries[];
// protected:
// static MessageHandlerSet MessageHandlers;
//-------------------------------------------------------------------
// Construction/destruction/verification
//-------------------------------------------------------------------
public:
LBE4ControlsManager();
~LBE4ControlsManager();
static Logical
TestClass();
Logical
TestInstance() const;
void
LocalShutdown();
void
Execute();
//-------------------------------------------------------------------
// Message handlers
//-------------------------------------------------------------------
// void
// BeginCalibrationMessageHandler(Receiver::Message *message);
// void
// EndCalibrationMessageHandler(Receiver::Message *message);
//
//-------------------------------------------------------------------
// Control mapping methods
//-------------------------------------------------------------------
void
Remove(ModeMask mode_mask);
static int
SetControlMappingID(
const char *control_name,
ControlsMapping *mapping
);
void
CreateStreamedMappings(
Entity *player,
const int *control_mappings
);
//-------------------------------------------------------------------
// Lamps
//-------------------------------------------------------------------
// Commonly used combinations
enum
{
LampSolidOff = RIO::solid + RIO::state1Off + RIO::state2Off,
LampSolidDim = RIO::solid + RIO::state1Dim + RIO::state2Dim,
LampSolidBright = RIO::solid + RIO::state1Bright + RIO::state2Bright
};
// This is the order of the mapped lamps
// See also "GetLampName"
enum {
LampAuxLowerRight8=0x00, // Lower left auxiliary panel buttons
LampAuxLowerRight7,
LampAuxLowerRight6,
LampAuxLowerRight5,
LampAuxLowerRight4,
LampAuxLowerRight3,
LampAuxLowerRight2,
LampAuxLowerRight1,
LampAuxLowerLeft8=0x08, // Lower right auxiliary panel buttons
LampAuxLowerLeft7,
LampAuxLowerLeft6,
LampAuxLowerLeft5,
LampAuxLowerLeft4,
LampAuxLowerLeft3,
LampAuxLowerLeft2,
LampAuxLowerLeft1,
LampSecondary1=0x10, // Secondary panel buttons
LampSecondary2,
LampSecondary3,
LampSecondary4,
LampSecondary5,
LampSecondary6,
LampTesla1, // top? of three solid-state relays
LampTesla2, // middle? of three solid-state relays
LampSecondary7=0x18,
LampSecondary8,
LampSecondary9,
LampSecondary10,
LampSecondary11,
LampSecondary12,
LampTesla3, // bottom of three solid-state relays
LampAuxUpperCenter8=0x20, // Upper center auxiliary panel buttons
LampAuxUpperCenter7,
LampAuxUpperCenter6,
LampAuxUpperCenter5,
LampAuxUpperCenter4,
LampAuxUpperCenter3,
LampAuxUpperCenter2,
LampAuxUpperCenter1,
LampAuxUpperLeft8=0x28, // Upper left auxiliary panel buttons
LampAuxUpperLeft7,
LampAuxUpperLeft6,
LampAuxUpperLeft5,
LampAuxUpperLeft4,
LampAuxUpperLeft3,
LampAuxUpperLeft2,
LampAuxUpperLeft1,
LampAuxUpperRight8=0x30, // Upper right auxiliary panel buttons
LampAuxUpperRight7,
LampAuxUpperRight6,
LampAuxUpperRight5,
LampAuxUpperRight4,
LampAuxUpperRight3,
LampAuxUpperRight2,
LampAuxUpperRight1,
LampIcomDecRelay = 0x38,
LampIcomAmpEnable = 0x39,
LampIcomIncRelay = 0x3A,
LampIcomPTTRelay = 0x03B,
LampPanic=0x3D,
LampFloor=0x3E, // floor lamp (entry/exit)
LampUnused=0x3F, // unimplemented convenience lamp
LampCount // the total number of defined lamps
};
Lamp *
MakeLinkedLamp(
int element,
ModeMask mode_mask = ModeManager::ModeAlwaysActive
);
void
SetLamp(
int lamp_number,
int state
);
static const char*
GetLampName(int lamp_number);
//-------------------------------------------------------------------
// Control groups
// Each control belongs to one of several groups (as in the
// enum below).
//-------------------------------------------------------------------
enum {
ScalarGroup,
JoystickGroup,
KeyboardGroup,
ButtonGroup,
MouseGroup
};
//-------------------------------------------------------------
// Scalars
//-------------------------------------------------------------
// This is the order of the mapped scalars
enum {
ScalarThrottle,
ScalarLeftPedal, ScalarRightPedal,
ScalarJoystickX, ScalarJoystickY, // 'real' joystick values
ScalarMouseX, ScalarMouseY, // 'virtual' joystick values
ScalarCount
};
ControlsUpdateManager<ControlsScalar>
scalarGroup[ScalarCount];
public:
void
SetThrottleDeadBand(Scalar dead_band);
void
SetPedalsDeadBand(Scalar dead_band);
//-------------------------------------------------------------
// Joysticks
//-------------------------------------------------------------
void
BeginAlignJoystick();
void
EndAlignJoystick();
void
CenterRIOJoystick();
void
SetJoystickDeadBand(Scalar dead_band);
// This is the order of the mapped joysticks
enum {
JoystickPhysical, // the 'real' joystick
JoystickVirtual, // a 'virtual' joystick driven by the PC mouse
JoystickCount
};
ControlsUpdateManager<ControlsJoystick>
joystickGroup[JoystickCount];
enum JoystickType
{
none, ordinary, flightstickPro, thrustMaster, directInputJoystick
};
JoystickType
joystickType;
protected:
Joystick
*joystickPointer;
int
virtualJoystickX, virtualJoystickY;
Scalar
lastJoystickUpdate;
public:
//-------------------------------------------------------------
// Keyboards
//-------------------------------------------------------------
// This is the order of the mapped keyboards
enum {
KeyboardPilot, // internal keypad used by the pilot
KeyboardExternal, // external keypad used by an operator
KeyboardPC, // PC keyboard
KeyboardCount
};
ControlsUpdateManager<ControlsKey>
keyboardGroup[KeyboardCount];
//-------------------------------------------------------------
// Buttons
//-------------------------------------------------------------
// This is the order of the buttons
enum {
ButtonAuxLowerRight8=0x00, // Lower left auxiliary panel buttons
ButtonAuxLowerRight7,
ButtonAuxLowerRight6,
ButtonAuxLowerRight5,
ButtonAuxLowerRight4,
ButtonAuxLowerRight3,
ButtonAuxLowerRight2,
ButtonAuxLowerRight1,
ButtonAuxLowerLeft8=0x08, // Lower right auxiliary panel buttons
ButtonAuxLowerLeft7,
ButtonAuxLowerLeft6,
ButtonAuxLowerLeft5,
ButtonAuxLowerLeft4,
ButtonAuxLowerLeft3,
ButtonAuxLowerLeft2,
ButtonAuxLowerLeft1,
ButtonSecondary1=0x10, // Secondary panel buttons
ButtonSecondary2,
ButtonSecondary3,
ButtonSecondary4,
ButtonSecondary5,
ButtonSecondary6,
ButtonSecondary7=0x18,
ButtonSecondary8,
ButtonSecondary9,
ButtonSecondary10,
ButtonSecondary11,
ButtonSecondary12,
ButtonAuxUpperCenter8=0x20,// Upper center auxiliary panel buttons
ButtonAuxUpperCenter7,
ButtonAuxUpperCenter6,
ButtonAuxUpperCenter5,
ButtonAuxUpperCenter4,
ButtonAuxUpperCenter3,
ButtonAuxUpperCenter2,
ButtonAuxUpperCenter1,
ButtonAuxUpperLeft8=0x28, // Upper left auxiliary panel buttons
ButtonAuxUpperLeft7,
ButtonAuxUpperLeft6,
ButtonAuxUpperLeft5,
ButtonAuxUpperLeft4,
ButtonAuxUpperLeft3,
ButtonAuxUpperLeft2,
ButtonAuxUpperLeft1,
ButtonAuxUpperRight8=0x30, // Upper right auxiliary panel buttons
ButtonAuxUpperRight7,
ButtonAuxUpperRight6,
ButtonAuxUpperRight5,
ButtonAuxUpperRight4,
ButtonAuxUpperRight3,
ButtonAuxUpperRight2,
ButtonAuxUpperRight1,
ButtonIcomHeadPluggedIn = 0x39,
ButtonIcomSensor = 0x3A,
ButtonIcomMikePluggedIn = 0x3B,
ButtonDoor=0x3C,
ButtonPanic=0x3D,
ButtonThrottle1=0x3F,
ButtonJoystickTrigger=0x40, // Joystick trigger button
FirstMappableButton=ButtonJoystickTrigger,
ButtonJoystickHatDown=0x41, // Joystick trigger hat down
ButtonJoystickHatUp=0x42, // Joystick trigger hat up
ButtonJoystickHatRight=0x43, // Joystick trigger hat left
ButtonJoystickHatLeft=0x44, // Joystick trigger hat left
ButtonJoystickPinky=0x45, // Joystick lowest button
ButtonJoystickThumbLow=0x46, // Joystick middle thumb button
ButtonJoystickThumbHigh=0x47, // Joystick upper thumb button (by hat)
LastMappableButton=ButtonJoystickThumbHigh,
// The PC keyboard buttons will eventually
// be added here.
ButtonMouseLeft, // left mouse button
ButtonMouseRight, // right mouse button
ButtonMouseCenter, // center mouse button (if available)
ButtonCount // defines number of buttons
};
ControlsUpdateManager<ControlsButton>
buttonGroup[ButtonCount];
protected:
ModeMask
buttonActivateModeMask[ButtonCount];
//-------------------------------------------------------------
// Mouse
//-------------------------------------------------------------
public:
ControlsUpdateManager<ControlsMouse>
mouseGroup[1]; // Yes, only one mouse. Get real.
protected:
Mouse
*mousePointer;
public:
//-------------------------------------------------------------
// Strings (matched keyboard input)
//-------------------------------------------------------------
ControlsStringManager
stringManager;
public:
//-------------------------------------------------------------------
// RIO data (serial hardware or the PadRIO pad/keyboard synthesizer)
//-------------------------------------------------------------------
RIOBase
*rioPointer;
protected:
Time
lastRioRequest;
public:
//-------------------------------------------------------------------
// Current IO type
//-------------------------------------------------------------------
enum IOType
{
None = 0,
PrimaryGenericJoystick,
PrimaryThrustMaster,
PrimaryFlightStickPro,
PrimaryRIO,
PrimaryDirectInputJoystick
};
IOType
primaryControlType;
//-------------------------------------------------------------------
// Existence flags
//-------------------------------------------------------------------
struct {
unsigned int mouseExists:1;
unsigned int keyboardExists:1;
unsigned int joystickExists:1;
unsigned int RIOExists:1;
}flags;
//-------------------------------------------------------------------
// Operator controls
//-------------------------------------------------------------------
protected:
enum L4ControlsMode
{
normalMode = 0,
calibrationMode
};
void
EnterControlsMode(
L4ControlsMode new_mode,
Scalar time_until_exit,
Receiver::MessageID message_on_exit
);
void
ExitControlsMode();
void
ManageControlsMode();
L4ControlsMode
l4ControlsMode;
Time
exitModeTime;
Receiver::MessageID
exitMessageID;
enum
{
maxStringIDs = 16
};
ControlsStringID
temporaryStringID[maxStringIDs];
//
//--------------------
// RIO Stream Spoofing
//--------------------
//
public:
Logical
GetNextRIOEvent(RIO::RIOEvent *rio_event);
void
ProcessRIOEvent(
RIO::RIOEvent &rio_event,
Logical *new_RIO_values
);
void
StartRecording()
{Check(this); streamStart = Now(); streamEnabled=True;}
void
SaveRecording(const char* file_name);
void
LoadPlayback(const char* file_name);
void
StartPlayback()
{Check(this); streamStart = Now(); streamEnabled=True;}
Logical
IsRecording()
{return rioStream && !playbackMode;}
Logical
IsPlayingBack()
{return rioStream && playbackMode;}
Logical
AnalogHasChanged();
protected:
MemoryStream
*rioStream;
Time
streamStart;
Logical
playbackMode;
Logical
streamEnabled;
Scalar
previousJoystickX,
previousJoystickY,
previousThrottle,
previousPedalLeft,
previousPedalRight;
};