Files
RP412/MUNGA_L4/L4CTRL.h
T
CydandClaude Fable 5 de5a97d37d Workstream A prototype: play without the cockpit
Splits the control surface the game consumes from the RIO board into
RIOBase (8 virtuals + the five analog scalars); the serial RIO is now one
implementation of it. Adds two new ones:

- PadRIO (L4CONTROLS=PAD): in-process RIO speaking the full surface from
  an XInput controller + PC keyboard using vRIO's default profile (left
  stick/WASD = stick, triggers/Q,E = pedals, right stick Y/PgUp,PgDn =
  rate throttle that holds position, A/Space = trigger, B/R = reverse,
  dpad/arrows = hat, Start,Back/F1,F2 = config). Samples in GetNextEvent
  so button latency does not depend on the 15 s menu-time analog cadence;
  hot-plugs pads; L4PADFLIP=XY inverts stick axes; lamp commands land in
  lampState[] for the planned on-screen cockpit panel. The stock
  VTVRIOMapper/lamp/button path runs unchanged.

- PlasmaScreen (L4PLASMA=SCREEN): the 128x32 plasma glass as a desktop
  window in plasma orange (L4PLASMASCALE, default x4), rendering the same
  Video8BitBuffered surface the gauge system always drew; no COM port.

Verified in the sandbox with vRIO off and no serial devices: boots to a
running mission, controller hot-detected, plasma window drawing live game
content (score readout). BUILD.md 4 documents the desktop environ.ini and
bindings; roadmap updated.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-12 13:13:37 -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;
};