The six PRESET switches down the map's right flank stored their lamps in modeLamp[], which holds four. Indices 4 and 5 ran off the end into presetLamp[0..1], so the whole thing stayed self-consistent by memory layout and nobody noticed - but it overwrote the four control-mode lamps made moments earlier, and BASIC/STANDARD/VETERAN/MASTER on the upper-right MFD were never lit again. presetLamp[], meanwhile, went unused. The preset pass now fills the array it was always meant to, and the lamp work moves out of the switch handler into a virtual NotifyOfPresetChange that PresetEnable announces itself. That closes the second gap in passing: keyboard 1-6 changed the mappings without touching the lamps, leaving the flank showing a preset that was no longer in force. Both routes now go through one place. The lamp arrays are also cleared in the constructor - only the mapping loops ever filled them, and NOMODES skips those. Verified by dumping the commanded RIO lamp states out of the running game (PadRIO, TEST.EGG, at rest in Basic mode). Before and after are identical except lamp 0x33, BASIC, which goes from 14 dim to 3c lit. The preset lamps are unchanged: they worked by accident, and now work by construction. docs/CONTROL-PRESETS.md is the research behind it. The presets are not a map feature at all - each is a complete factory layout for the four mappable stick buttons, one mode-mask bit apiece, with all 26 vehicles carrying their own six-preset table in RPL4.RES for both the pod RIO and the Thrustmaster. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2098 lines
49 KiB
C++
2098 lines
49 KiB
C++
#include "rpl4.h"
|
|
#pragma hdrstop
|
|
|
|
#include "..\rp\chute.h"
|
|
#include "rpl4mppr.h"
|
|
#include "rpl4grnd.h"
|
|
#include "rpl4app.h"
|
|
#include "rpl4mode.h"
|
|
#include "..\munga_l4\l4ctrl.h"
|
|
#include "..\munga_l4\l4keybd.h"
|
|
#include "..\rp\booster.h"
|
|
#include "..\rp\vtv.h"
|
|
#include "..\munga\appmsg.h"
|
|
|
|
L4Lamp *
|
|
CreateControlledLamp(
|
|
int lamp_number,
|
|
ModeMask mode_mask=RPL4ModeManager::ModeAlwaysActive
|
|
)
|
|
{
|
|
Check(application);
|
|
|
|
if (application->GetGaugeRenderer() == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
else
|
|
{
|
|
Check(application->GetGaugeRenderer());
|
|
|
|
//------------------------------------------
|
|
// Create the lamp
|
|
//------------------------------------------
|
|
L4LampManager
|
|
*lamp_manager = (L4LampManager *) application->
|
|
GetGaugeRenderer()->GetLampManager();
|
|
Check(lamp_manager);
|
|
|
|
L4Lamp
|
|
*lamp = new L4Lamp(
|
|
lamp_number,
|
|
mode_mask,
|
|
lamp_manager
|
|
);
|
|
Register_Object(lamp);
|
|
|
|
lamp->SetAutomaticOperation(False);
|
|
|
|
Check_Fpu();
|
|
return lamp;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
void
|
|
MakeButtonLampEvent(
|
|
LBE4ControlsManager *controls,
|
|
int button_number,
|
|
Receiver *receiver,
|
|
Receiver::MessageID message_id,
|
|
Plug *dependant,
|
|
ModeMask mode_mask = RPL4ModeManager::ModeAlwaysActive
|
|
)
|
|
{
|
|
Check(controls);
|
|
Check(receiver);
|
|
Check(dependant);
|
|
|
|
controls->buttonGroup[button_number].Add(
|
|
mode_mask,
|
|
receiver,
|
|
message_id,
|
|
dependant
|
|
);
|
|
controls->MakeLinkedLamp(button_number, mode_mask);
|
|
}
|
|
|
|
void
|
|
MakeButtonLampEventPair(
|
|
LBE4ControlsManager *controls,
|
|
int button_number1,
|
|
int button_number2,
|
|
Receiver *receiver,
|
|
Receiver::MessageID message_id,
|
|
Plug *dependant,
|
|
ModeMask mode_mask = RPL4ModeManager::ModeAlwaysActive
|
|
)
|
|
{
|
|
MakeButtonLampEvent(
|
|
controls,
|
|
button_number1,
|
|
receiver,
|
|
message_id,
|
|
dependant,
|
|
mode_mask
|
|
);
|
|
MakeButtonLampEvent(
|
|
controls,
|
|
button_number2,
|
|
receiver,
|
|
message_id,
|
|
dependant,
|
|
mode_mask
|
|
);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
void
|
|
MakeButtonLampDirect(
|
|
LBE4ControlsManager *controls,
|
|
int button_number,
|
|
ControlsButton *direct_target,
|
|
Plug *dependent,
|
|
ModeMask mode_mask = RPL4ModeManager::ModeAlwaysActive
|
|
)
|
|
{
|
|
Check(controls);
|
|
|
|
controls->buttonGroup[button_number].Add(
|
|
mode_mask,
|
|
direct_target,
|
|
dependent
|
|
);
|
|
controls->MakeLinkedLamp(button_number, mode_mask);
|
|
}
|
|
|
|
void
|
|
MakeButtonLampDirectPair(
|
|
LBE4ControlsManager *controls,
|
|
int button_number1,
|
|
int button_number2,
|
|
ControlsButton *direct_target,
|
|
Plug *dependent,
|
|
ModeMask mode_mask = RPL4ModeManager::ModeAlwaysActive
|
|
)
|
|
{
|
|
MakeButtonLampDirect(
|
|
controls,
|
|
button_number1,
|
|
direct_target,
|
|
dependent,
|
|
mode_mask
|
|
);
|
|
MakeButtonLampDirect(
|
|
controls,
|
|
button_number2,
|
|
direct_target,
|
|
dependent,
|
|
mode_mask
|
|
);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
void
|
|
CreateDirectSystemMapping(
|
|
LBE4ControlsManager *controls,
|
|
int button_number,
|
|
ControlsButton *direct_target,
|
|
Receiver *receiver,
|
|
Receiver::MessageID configure_message_id
|
|
)
|
|
{
|
|
Check(controls);
|
|
Check(receiver);
|
|
|
|
//-------------------------------------------------
|
|
// When not configuring, press button to activate
|
|
//-------------------------------------------------
|
|
MakeButtonLampDirect(
|
|
controls,
|
|
button_number,
|
|
direct_target,
|
|
receiver,
|
|
RPL4ModeManager::ModeNonConfig
|
|
);
|
|
//-------------------------------------------------
|
|
// When configuring, the same button allows mapping
|
|
//-------------------------------------------------
|
|
MakeButtonLampEvent(
|
|
controls,
|
|
button_number,
|
|
receiver,
|
|
configure_message_id,
|
|
receiver,
|
|
RPL4ModeManager::ModeConfigReady
|
|
);
|
|
}
|
|
|
|
void
|
|
CreateDirectSystemMappingPair(
|
|
LBE4ControlsManager *controls,
|
|
int button_number1,
|
|
int button_number2,
|
|
ControlsButton *direct_target,
|
|
Receiver *receiver,
|
|
Receiver::MessageID configure_message_id
|
|
)
|
|
{
|
|
Check(controls);
|
|
Check(receiver);
|
|
|
|
CreateDirectSystemMapping(
|
|
controls,
|
|
button_number1,
|
|
direct_target,
|
|
receiver,
|
|
configure_message_id
|
|
);
|
|
CreateDirectSystemMapping(
|
|
controls,
|
|
button_number2,
|
|
direct_target,
|
|
receiver,
|
|
configure_message_id
|
|
);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
void
|
|
CreateEventSystemMapping(
|
|
LBE4ControlsManager *controls,
|
|
int button_number,
|
|
Receiver *receiver,
|
|
Receiver::MessageID active_message_id,
|
|
Receiver::MessageID configure_message_id
|
|
)
|
|
{
|
|
Check(controls);
|
|
Check(receiver);
|
|
|
|
//-------------------------------------------------
|
|
// When not configuring, press button to activate
|
|
//-------------------------------------------------
|
|
MakeButtonLampEvent(
|
|
controls,
|
|
button_number,
|
|
receiver,
|
|
active_message_id,
|
|
receiver,
|
|
RPL4ModeManager::ModeNonConfig
|
|
);
|
|
//-------------------------------------------------
|
|
// When configuring, the same button allows mapping
|
|
//-------------------------------------------------
|
|
MakeButtonLampEvent(
|
|
controls,
|
|
button_number,
|
|
receiver,
|
|
configure_message_id,
|
|
receiver,
|
|
RPL4ModeManager::ModeConfigReady
|
|
);
|
|
}
|
|
|
|
void
|
|
CreateEventSystemMappingPair(
|
|
LBE4ControlsManager *controls,
|
|
int button_number1,
|
|
int button_number2,
|
|
Receiver *receiver,
|
|
Receiver::MessageID active_message_id,
|
|
Receiver::MessageID configure_message_id
|
|
)
|
|
{
|
|
CreateEventSystemMapping(
|
|
controls,
|
|
button_number1,
|
|
receiver,
|
|
active_message_id,
|
|
configure_message_id
|
|
);
|
|
CreateEventSystemMapping(
|
|
controls,
|
|
button_number2,
|
|
receiver,
|
|
active_message_id,
|
|
configure_message_id
|
|
);
|
|
}
|
|
|
|
|
|
//#############################################################################
|
|
//########################### L4VTVControlsMapper ##############################
|
|
//#############################################################################
|
|
|
|
//#############################################################################
|
|
// Shared Data Support
|
|
//
|
|
L4VTVControlsMapper::SharedData
|
|
L4VTVControlsMapper::DefaultData(
|
|
L4VTVControlsMapper::GetClassDerivations(),
|
|
L4VTVControlsMapper::GetMessageHandlers(),
|
|
L4VTVControlsMapper::GetAttributeIndex(),
|
|
L4VTVControlsMapper::StateCount
|
|
);
|
|
|
|
Derivation* L4VTVControlsMapper::GetClassDerivations()
|
|
{ static Derivation classDerivations(VTVControlsMapper::GetClassDerivations(), "L4VTVControlsMapper");
|
|
return &classDerivations;
|
|
}
|
|
|
|
|
|
//#############################################################################
|
|
// Messaging Support
|
|
//
|
|
const Receiver::HandlerEntry
|
|
L4VTVControlsMapper::MessageHandlerEntries[]=
|
|
{
|
|
MESSAGE_ENTRY(L4VTVControlsMapper, Keypress)
|
|
};
|
|
|
|
Receiver::MessageHandlerSet& L4VTVControlsMapper::GetMessageHandlers()
|
|
{
|
|
static Receiver::MessageHandlerSet messageHandlers(ELEMENTS(L4VTVControlsMapper::MessageHandlerEntries), L4VTVControlsMapper::MessageHandlerEntries, VTVControlsMapper::GetMessageHandlers());
|
|
return messageHandlers;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
L4VTVControlsMapper::KeypressMessageHandler(
|
|
ReceiverDataMessageOf<ControlsKey> *message
|
|
)
|
|
{
|
|
Check(this);
|
|
|
|
Check(message);
|
|
|
|
switch (message->dataContents)
|
|
{
|
|
case PCK_F11:
|
|
{
|
|
Check(application);
|
|
|
|
GaugeRenderer
|
|
*renderer = application->GetGaugeRenderer();
|
|
if (renderer != NULL)
|
|
{
|
|
Check(renderer);
|
|
renderer->ProfileReport();
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
Check_Fpu();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
L4VTVControlsMapper::SetControlsMode(int new_mode)
|
|
{
|
|
Check(this);
|
|
|
|
//-------------------------------------------
|
|
// Get mode manager
|
|
//-------------------------------------------
|
|
Check(application);
|
|
|
|
RPL4ModeManager
|
|
*mode_manager = Cast_Object(
|
|
RPL4ModeManager*,
|
|
application->GetModeManager()
|
|
);
|
|
Check(mode_manager);
|
|
|
|
int
|
|
previous_mode = controlMode;
|
|
//-------------------------------------------
|
|
// Change modes
|
|
//-------------------------------------------
|
|
switch (new_mode)
|
|
{
|
|
case BasicMode:
|
|
//--------------------------------------
|
|
// If we aren't already in basic mode,
|
|
// switch us there
|
|
//--------------------------------------
|
|
if (controlMode != BasicMode)
|
|
{
|
|
controlMode = BasicMode;
|
|
mode_manager->AddModeMask(RPL4ModeManager::ModeBasic);
|
|
mode_manager->RemoveModeMask(RPL4ModeManager::ModeStandard);
|
|
//----------------------------------------
|
|
// Ensure configuring is turned off
|
|
//----------------------------------------
|
|
if (GetSimulationState() == ConfigurationState)
|
|
{
|
|
mode_manager->AddModeMask(RPL4ModeManager::ModeNonConfig);
|
|
mode_manager->RemoveModeMask(RPL4ModeManager::ModeConfiguring);
|
|
|
|
SetSimulationState(DefaultState);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case StandardMode:
|
|
//---------------------------------------
|
|
// If we aren't already in standard mode,
|
|
// switch us there
|
|
//---------------------------------------
|
|
if (controlMode != StandardMode)
|
|
{
|
|
controlMode = StandardMode;
|
|
mode_manager->AddModeMask(RPL4ModeManager::ModeStandard);
|
|
mode_manager->RemoveModeMask(RPL4ModeManager::ModeBasic);
|
|
}
|
|
break;
|
|
|
|
case VeteranMode:
|
|
//---------------------------------------
|
|
// If we aren't already in veteran mode,
|
|
// switch us there
|
|
//---------------------------------------
|
|
if (controlMode != VeteranMode)
|
|
{
|
|
controlMode = VeteranMode;
|
|
mode_manager->AddModeMask(RPL4ModeManager::ModeStandard);
|
|
mode_manager->RemoveModeMask(RPL4ModeManager::ModeBasic);
|
|
}
|
|
break;
|
|
|
|
case MasterMode:
|
|
//---------------------------------------
|
|
// If we aren't already in master mode,
|
|
// switch us there
|
|
//---------------------------------------
|
|
if (controlMode != MasterMode)
|
|
{
|
|
controlMode = MasterMode;
|
|
mode_manager->AddModeMask(RPL4ModeManager::ModeStandard);
|
|
mode_manager->RemoveModeMask(RPL4ModeManager::ModeBasic);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
Tell(
|
|
"L4VTVControlsMapper::SetControlsMode(" << new_mode <<
|
|
")\n"
|
|
);
|
|
Fail("out of range");
|
|
break;
|
|
}
|
|
//----------------------------------------
|
|
// Notify of mode change
|
|
//----------------------------------------
|
|
if (previous_mode != controlMode)
|
|
{
|
|
L4VTVControlsMapper::NotifyOfControlModeChange(controlMode);
|
|
}
|
|
Check_Fpu();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
L4VTVControlsMapper::SetConfigurationMode(Logical new_state)
|
|
{
|
|
Check(this);
|
|
|
|
//-------------------------------------------
|
|
// Call ancestral method
|
|
//-------------------------------------------
|
|
VTVControlsMapper::SetConfigurationState(new_state);
|
|
|
|
//-------------------------------------------
|
|
// Get mode manager
|
|
//-------------------------------------------
|
|
Check(application);
|
|
|
|
RPL4ModeManager
|
|
*mode_manager = Cast_Object(
|
|
RPL4ModeManager*,
|
|
application->GetModeManager()
|
|
);
|
|
Check(mode_manager);
|
|
|
|
//-------------------------------------------
|
|
// Modify mode
|
|
//-------------------------------------------
|
|
if (GetSimulationState() == ConfigurationState)
|
|
{
|
|
mode_manager->RemoveModeMask(RPL4ModeManager::ModeAllNonConfig);
|
|
// Note: this removes ALL presets as well
|
|
|
|
mode_manager->AddModeMask(RPL4ModeManager::ModeConfigReady);
|
|
}
|
|
else
|
|
{
|
|
mode_manager->RemoveModeMask(RPL4ModeManager::ModeConfigReady);
|
|
|
|
mode_manager->AddModeMask(RPL4ModeManager::ModeNonConfig);
|
|
mode_manager->AddModeMask(previousPresetModeMask);
|
|
}
|
|
Check_Fpu();
|
|
}
|
|
|
|
|
|
//#############################################################################
|
|
// Platform-specific updates
|
|
//
|
|
void
|
|
L4VTVControlsMapper::NotifyOfControlModeChange(int new_mode)
|
|
{
|
|
Check(this);
|
|
// The gauges are controlled directly by modes, so we do nothing here.
|
|
|
|
//-----------------------------------
|
|
// Invoke ancestral method
|
|
//-----------------------------------
|
|
VTVControlsMapper::NotifyOfControlModeChange(new_mode);
|
|
|
|
Check_Fpu();
|
|
}
|
|
|
|
void
|
|
L4VTVControlsMapper::NotifyOfConfigurationModeChange(Logical new_state)
|
|
{
|
|
Check(this);
|
|
// The gauges are controlled directly by modes, so we do nothing here.
|
|
|
|
//-----------------------------------
|
|
// Invoke ancestral method
|
|
//-----------------------------------
|
|
VTVControlsMapper::NotifyOfConfigurationModeChange(new_state);
|
|
|
|
Check_Fpu();
|
|
}
|
|
|
|
//#############################################################################
|
|
// Construction and Destruction Support
|
|
//
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
L4VTVControlsMapper::L4VTVControlsMapper(
|
|
VTV *player_vehicle,
|
|
int subsystem_ID,
|
|
SubsystemResource *subsystem_resource,
|
|
SharedData &shared_data
|
|
):
|
|
VTVControlsMapper(
|
|
player_vehicle,
|
|
subsystem_ID,
|
|
subsystem_resource,
|
|
shared_data
|
|
)
|
|
{
|
|
Check(application);
|
|
|
|
RPL4ModeManager
|
|
*mode_manager = Cast_Object(
|
|
RPL4ModeManager*,
|
|
application->GetModeManager()
|
|
);
|
|
Check(mode_manager);
|
|
//------------------------------------------
|
|
// Initialize presets
|
|
//------------------------------------------
|
|
previousPresetModeMask = RPL4ModeManager::ModePreset1;
|
|
previousPresetNumber = 0;
|
|
|
|
mode_manager->AddModeMask(previousPresetModeMask);
|
|
Check_Fpu();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
L4VTVControlsMapper::~L4VTVControlsMapper()
|
|
{
|
|
Check_Fpu();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
Logical
|
|
L4VTVControlsMapper::TestInstance() const
|
|
{
|
|
return IsDerivedFrom(*GetClassDerivations());
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
L4VTVControlsMapper::CreateTemporaryEventMappings(
|
|
Receiver *receiver,
|
|
Receiver::MessageID config_message_id
|
|
)
|
|
{
|
|
Check(this);
|
|
Check(receiver);
|
|
|
|
Check(application);
|
|
LBE4ControlsManager
|
|
*controls_mgr = Cast_Object(
|
|
LBE4ControlsManager *,
|
|
application->GetControlsManager()
|
|
);
|
|
Check(controls_mgr);
|
|
|
|
//------------------------------------------------
|
|
// Create temporary mappings for configuration
|
|
//------------------------------------------------
|
|
for(
|
|
int i=LBE4ControlsManager::FirstMappableButton;
|
|
i<=LBE4ControlsManager::LastMappableButton;
|
|
++i
|
|
)
|
|
{
|
|
//---------------------------------
|
|
// Skip past hat switches
|
|
//---------------------------------
|
|
if (i == LBE4ControlsManager::ButtonJoystickHatDown)
|
|
{
|
|
i = LBE4ControlsManager::ButtonJoystickPinky;
|
|
}
|
|
|
|
controls_mgr->buttonGroup[i].Add(
|
|
RPL4ModeManager::ModeConfigReady,
|
|
receiver,
|
|
config_message_id,
|
|
receiver
|
|
);
|
|
}
|
|
Check_Fpu();
|
|
}
|
|
|
|
void
|
|
L4VTVControlsMapper::RemoveTemporaryEventMappings()
|
|
{
|
|
Check(this);
|
|
|
|
Check(application);
|
|
LBE4ControlsManager
|
|
*controls_mgr = Cast_Object(
|
|
LBE4ControlsManager *,
|
|
application->GetControlsManager()
|
|
);
|
|
Check(controls_mgr);
|
|
|
|
//------------------------------------------------
|
|
// Remove temporary mappings
|
|
//------------------------------------------------
|
|
for(
|
|
int i=LBE4ControlsManager::FirstMappableButton;
|
|
i<=LBE4ControlsManager::LastMappableButton;
|
|
++i
|
|
)
|
|
{
|
|
//---------------------------------
|
|
// Skip past hat switches
|
|
//---------------------------------
|
|
if (i == LBE4ControlsManager::ButtonJoystickHatDown)
|
|
{
|
|
i = LBE4ControlsManager::ButtonJoystickPinky;
|
|
}
|
|
|
|
controls_mgr->buttonGroup[i].Remove(RPL4ModeManager::ModeConfigReady);
|
|
}
|
|
Check_Fpu();
|
|
}
|
|
|
|
void
|
|
L4VTVControlsMapper::AddOrErase(
|
|
unsigned int /*button_ID*/,
|
|
Receiver */*target*/,
|
|
Receiver::MessageID /*message_ID*/
|
|
)
|
|
{
|
|
Check_Fpu();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
L4VTVControlsMapper::AddOrErase(
|
|
unsigned int /*button_ID*/,
|
|
ControlsButton */*destination*/
|
|
)
|
|
{
|
|
Check_Fpu();
|
|
}
|
|
|
|
void
|
|
L4VTVControlsMapper::PresetEnable(int preset_number)
|
|
{
|
|
Check(this);
|
|
|
|
static ModeMask
|
|
mode_table[presetCount] =
|
|
{
|
|
RPL4ModeManager::ModePreset1,
|
|
RPL4ModeManager::ModePreset2,
|
|
RPL4ModeManager::ModePreset3,
|
|
RPL4ModeManager::ModePreset4,
|
|
RPL4ModeManager::ModePreset5,
|
|
RPL4ModeManager::ModePreset6
|
|
};
|
|
|
|
mustMatch = preset_number; // HACK - for backward watcher compatibility
|
|
|
|
if (preset_number != previousPresetNumber)
|
|
{
|
|
Check(application);
|
|
|
|
RPL4ModeManager
|
|
*mode_manager = Cast_Object(
|
|
RPL4ModeManager*,
|
|
application->GetModeManager()
|
|
);
|
|
Check(mode_manager);
|
|
|
|
//-----------------------------------
|
|
// Disable the old presets
|
|
//-----------------------------------
|
|
mode_manager->RemoveModeMask(previousPresetModeMask);
|
|
//-----------------------------------
|
|
// Enable the new presets
|
|
//-----------------------------------
|
|
if (preset_number >= 0)
|
|
{
|
|
Verify(preset_number < presetCount);
|
|
|
|
previousPresetModeMask = mode_table[preset_number];
|
|
mode_manager->AddModeMask(previousPresetModeMask);
|
|
}
|
|
//-----------------------------------
|
|
// Move the lamps with the mappings.
|
|
// Doing it here rather than in the
|
|
// switch handler keeps the keyboard
|
|
// presets (1-6) in step as well.
|
|
//-----------------------------------
|
|
NotifyOfPresetChange(previousPresetNumber, preset_number);
|
|
//-----------------------------------
|
|
// Save the new preset number
|
|
//-----------------------------------
|
|
previousPresetNumber = preset_number;
|
|
}
|
|
|
|
Check_Fpu();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
L4VTVControlsMapper::NotifyOfPresetChange(
|
|
int /*old_preset*/,
|
|
int /*new_preset*/
|
|
)
|
|
{
|
|
Check(this);
|
|
// The base mapper has no preset lamps to move.
|
|
Check_Fpu();
|
|
}
|
|
|
|
//#############################################################################
|
|
//########################### ThrustmasterMapper ##############################
|
|
//#############################################################################
|
|
|
|
//#############################################################################
|
|
// Shared Data Support
|
|
//
|
|
VTVThrustmasterMapper::SharedData
|
|
VTVThrustmasterMapper::DefaultData(
|
|
VTVThrustmasterMapper::GetClassDerivations(),
|
|
VTVThrustmasterMapper::MessageHandlers,
|
|
VTVThrustmasterMapper::GetAttributeIndex(),
|
|
VTVThrustmasterMapper::StateCount
|
|
);
|
|
|
|
Derivation* VTVThrustmasterMapper::GetClassDerivations()
|
|
{ static Derivation classDerivations(L4VTVControlsMapper::GetClassDerivations(), "VTVThrustmasterMapper");
|
|
return &classDerivations;
|
|
}
|
|
|
|
|
|
//#############################################################################
|
|
// Messaging Support
|
|
//
|
|
const Receiver::HandlerEntry
|
|
VTVThrustmasterMapper::MessageHandlerEntries[]=
|
|
{
|
|
MESSAGE_ENTRY(VTVThrustmasterMapper, Keypress)
|
|
};
|
|
|
|
Receiver::MessageHandlerSet
|
|
VTVThrustmasterMapper::MessageHandlers(
|
|
ELEMENTS(VTVThrustmasterMapper::MessageHandlerEntries),
|
|
VTVThrustmasterMapper::MessageHandlerEntries,
|
|
L4VTVControlsMapper::GetMessageHandlers()
|
|
);
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
VTVThrustmasterMapper::KeypressMessageHandler(
|
|
ReceiverDataMessageOf<ControlsKey> *message
|
|
)
|
|
{
|
|
Check(this);
|
|
Check(message);
|
|
LBE4ControlsManager
|
|
*controls =
|
|
Cast_Object(LBE4ControlsManager*,application->GetControlsManager());
|
|
Check(controls);
|
|
|
|
switch (message->dataContents)
|
|
{
|
|
//-------------------------------------------------------
|
|
// Joystick
|
|
//-------------------------------------------------------
|
|
case PCK_F1:
|
|
Tell("Aligning Joystick...\n");
|
|
controls->BeginAlignJoystick();
|
|
GetEntity()->SetSimulationState(VTV::StasisState);
|
|
break;
|
|
case PCK_F2:
|
|
Tell("Joystick Aligned...\n");
|
|
controls->EndAlignJoystick();
|
|
GetEntity()->SetSimulationState(VTV::DefaultState);
|
|
break;
|
|
|
|
case PCK_F12:
|
|
//-------------------------------------------------------
|
|
// Exit
|
|
//-------------------------------------------------------
|
|
Check(application);
|
|
if (
|
|
application->GetApplicationState() != Application::EndingMission
|
|
&& application->GetApplicationState() != Application::StoppingMission
|
|
)
|
|
{
|
|
DEBUG_STREAM << "Keystroke invoking mission stop...\n" << std::flush;
|
|
Exit_Code = 1;
|
|
Application::StopMissionMessage message(Exit_Code);
|
|
application->Dispatch(&message);
|
|
}
|
|
break;
|
|
|
|
//-------------------------------------------------------
|
|
// Control eyepoint views
|
|
//-------------------------------------------------------
|
|
case PCK_UP: // look forward
|
|
lookLeft = -1;
|
|
lookRight = -1;
|
|
lookBehind = -1;
|
|
lookUp = -1;
|
|
break;
|
|
case PCK_LEFT: // look left
|
|
lookLeft = 1;
|
|
lookRight = -1;
|
|
lookBehind = -1;
|
|
lookUp = -1;
|
|
break;
|
|
case PCK_RIGHT: // look right
|
|
lookLeft = -1;
|
|
lookRight = 1;
|
|
lookBehind = -1;
|
|
lookUp = -1;
|
|
break;
|
|
case PCK_DOWN: // look behind
|
|
lookLeft = -1;
|
|
lookRight = -1;
|
|
lookBehind = 1;
|
|
lookUp = -1;
|
|
break;
|
|
case PCK_CTL_UP: // look up
|
|
lookLeft = -1;
|
|
lookRight = -1;
|
|
lookBehind = -1;
|
|
lookUp = 1;
|
|
break;
|
|
|
|
//-------------------------------------------------------
|
|
// Set driving modes
|
|
//-------------------------------------------------------
|
|
case 'b':
|
|
case 'B': SetControlsMode(BasicMode); break;
|
|
|
|
case 's':
|
|
case 'S': SetControlsMode(StandardMode); break;
|
|
|
|
case 'v':
|
|
case 'V': SetControlsMode(VeteranMode); break;
|
|
|
|
case 'm':
|
|
case 'M': SetControlsMode(MasterMode); break;
|
|
|
|
//-------------------------------------------------------
|
|
// Configuration stuff
|
|
//-------------------------------------------------------
|
|
case 'c':
|
|
case 'C':
|
|
//------------------------------
|
|
// Not allowed in 'basic' mode
|
|
//------------------------------
|
|
if (controlMode != BasicMode)
|
|
{
|
|
static Logical
|
|
config_state = False;
|
|
//----------------------------
|
|
// Invert configuration state
|
|
//----------------------------
|
|
config_state ^= True;
|
|
//----------------------------
|
|
// Send message to base class
|
|
//----------------------------
|
|
SetConfigurationMode(config_state);
|
|
}
|
|
break;
|
|
|
|
//-------------------------------------------------------
|
|
// Map scale stuff
|
|
//-------------------------------------------------------
|
|
case '<':
|
|
case ',':
|
|
{
|
|
ReceiverDataMessageOf<ControlsButton>
|
|
config_msg(
|
|
VTV::ZoomInMessageID,
|
|
sizeof(ReceiverDataMessageOf<ControlsButton>),
|
|
1
|
|
);
|
|
GetEntity()->Dispatch(&config_msg);
|
|
break;
|
|
}
|
|
|
|
case '>':
|
|
case '.':
|
|
{
|
|
ReceiverDataMessageOf<ControlsButton>
|
|
config_msg(
|
|
VTV::ZoomOutMessageID,
|
|
sizeof(ReceiverDataMessageOf<ControlsButton>),
|
|
1
|
|
);
|
|
GetEntity()->Dispatch(&config_msg);
|
|
break;
|
|
}
|
|
|
|
case 'h':
|
|
case 'H':
|
|
//-------------------------------------------------------
|
|
// Horn blast
|
|
//-------------------------------------------------------
|
|
{
|
|
static ControlsButton horn_state=1;
|
|
ReceiverDataMessageOf<ControlsButton>
|
|
config_msg(
|
|
ActivateHornMessageID,
|
|
sizeof(ReceiverDataMessageOf<ControlsButton>),
|
|
horn_state
|
|
);
|
|
Dispatch(&config_msg);
|
|
horn_state = -horn_state;
|
|
break;
|
|
}
|
|
|
|
case 'z':
|
|
case 'Z':
|
|
//---------------------------------------------------------
|
|
// Chute
|
|
//---------------------------------------------------------
|
|
{
|
|
ReceiverDataMessageOf<ControlsButton>
|
|
config_msg(
|
|
Chute::ActivateMessageID,
|
|
sizeof(ReceiverDataMessageOf<ControlsButton>),
|
|
1
|
|
);
|
|
for (int i=0; i < GetEntity()->GetSubsystemCount(); i++)
|
|
{
|
|
Subsystem *sub = GetEntity()->GetSubsystem(i);
|
|
if (sub && sub->GetClassID() == VTV::ChuteClassID)
|
|
{
|
|
sub->Dispatch(&config_msg);
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case 'w':
|
|
case 'W':
|
|
//---------------------------------------------------------
|
|
// Booster
|
|
//---------------------------------------------------------
|
|
{
|
|
ReceiverDataMessageOf<ControlsButton>
|
|
config_msg(
|
|
Booster::ActivateMessageID,
|
|
sizeof(ReceiverDataMessageOf<ControlsButton>),
|
|
1
|
|
);
|
|
for (int i=0; i < GetEntity()->GetSubsystemCount(); i++)
|
|
{
|
|
Subsystem *sub = GetEntity()->GetSubsystem(i);
|
|
if (sub && sub->GetClassID() == VTV::BoosterClassID)
|
|
{
|
|
sub->Dispatch(&config_msg);
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case 'd':
|
|
case 'D':
|
|
//---------------------------------------------------------
|
|
// Demopack
|
|
//---------------------------------------------------------
|
|
{
|
|
/*for (int i=0; i < GetEntity()->GetSubsystemCount(); i++)
|
|
{
|
|
Subsystem *sub = GetEntity()->GetSubsystem(i);
|
|
if (sub && sub->GetClassID() == VTV::DemolitionPackDropperClassID)
|
|
{
|
|
((DemolitionPackDropper*)sub)->Fire();
|
|
break;
|
|
}
|
|
}*/
|
|
}
|
|
|
|
case 'r':
|
|
case 'R':
|
|
//-------------------------------------------------------
|
|
// Toggle reticle
|
|
//-------------------------------------------------------
|
|
{
|
|
ReceiverDataMessageOf<ControlsButton>
|
|
config_msg(
|
|
ToggleReticleMessageID,
|
|
sizeof(ReceiverDataMessageOf<ControlsButton>),
|
|
1
|
|
);
|
|
Dispatch(&config_msg);
|
|
break;
|
|
}
|
|
//-------------------------------------------------------
|
|
// Trigger presets
|
|
//-------------------------------------------------------
|
|
case '1':
|
|
case '2':
|
|
case '3':
|
|
case '4':
|
|
case '5':
|
|
case '6':
|
|
PresetEnable(message->dataContents - '1');
|
|
break;
|
|
|
|
//-------------------------------------------------------
|
|
// Suspend/resume
|
|
//-------------------------------------------------------
|
|
case '[':
|
|
{
|
|
Check(application);
|
|
|
|
Enumeration
|
|
state = application->GetApplicationState();
|
|
|
|
if (
|
|
state != Application::EndingMission &&
|
|
state != Application::StoppingMission &&
|
|
state != Application::SuspendingMission
|
|
)
|
|
{
|
|
DEBUG_STREAM << "Keystroke invoking mission suspend...\n" << std::flush;
|
|
Application::SuspendMissionMessage message;
|
|
application->Dispatch(&message);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case ']':
|
|
Check(application);
|
|
if (
|
|
application->GetApplicationState() == Application::SuspendingMission
|
|
)
|
|
{
|
|
DEBUG_STREAM << "Keystroke invoking mission resume...\n" << std::flush;
|
|
Application::ResumeMissionMessage message;
|
|
application->Dispatch(&message);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
L4VTVControlsMapper::KeypressMessageHandler(message);
|
|
break;
|
|
}
|
|
Check_Fpu();
|
|
}
|
|
|
|
//#############################################################################
|
|
// Model Support
|
|
//
|
|
void
|
|
VTVThrustmasterMapper::InterpretControls(Scalar time_slice)
|
|
{
|
|
//
|
|
//----------------------------------------
|
|
// Use the hat buttons to set the throttle
|
|
//----------------------------------------
|
|
//
|
|
LBE4ControlsManager
|
|
*controls = Cast_Object(
|
|
LBE4ControlsManager*,
|
|
application->GetControlsManager()
|
|
);
|
|
Check(controls);
|
|
|
|
throttlePosition = controls->scalarGroup[LBE4ControlsManager::ScalarThrottle].previousValue;
|
|
/*if (throttleUp > 0)
|
|
{
|
|
throttlePosition = 1.0f;
|
|
reverseThrust = 0;
|
|
}
|
|
else if (throttleDown > 0)
|
|
{
|
|
throttlePosition = 1.0f;
|
|
reverseThrust = 1;
|
|
}
|
|
else
|
|
{
|
|
throttlePosition = 0.0f;
|
|
reverseThrust = 0;
|
|
}*/
|
|
|
|
//
|
|
//----------------------------------------
|
|
// Sum the pedal inputs
|
|
//----------------------------------------
|
|
//
|
|
pedalsPosition = leftPedal - rightPedal;
|
|
|
|
|
|
VTVControlsMapper::InterpretControls(time_slice);
|
|
Check_Fpu();
|
|
}
|
|
|
|
//#############################################################################
|
|
// Construction and Destruction Support
|
|
//
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
VTVThrustmasterMapper::VTVThrustmasterMapper(
|
|
VTV *player_vehicle,
|
|
int subsystem_ID,
|
|
SubsystemResource *subsystem_resource
|
|
):
|
|
L4VTVControlsMapper(
|
|
player_vehicle,
|
|
subsystem_ID,
|
|
subsystem_resource,
|
|
DefaultData
|
|
)
|
|
{
|
|
SetPerformance(&VTVThrustmasterMapper::InterpretControls);
|
|
leftPedal = 0.0f;
|
|
rightPedal = 0.0f;
|
|
|
|
LBE4ControlsManager
|
|
*controls =
|
|
Cast_Object(LBE4ControlsManager*,application->GetControlsManager());
|
|
Check(controls);
|
|
|
|
controls
|
|
->joystickGroup[LBE4ControlsManager::JoystickPhysical]
|
|
.Add(RPL4ModeManager::ModeAlwaysActive, &stickPosition, this);
|
|
|
|
//
|
|
//--------------
|
|
// Pedal mapping
|
|
//--------------
|
|
//
|
|
controls
|
|
->scalarGroup[LBE4ControlsManager::ScalarLeftPedal]
|
|
.Add(RPL4ModeManager::ModeAlwaysActive, &leftPedal, this);
|
|
|
|
controls
|
|
->scalarGroup[LBE4ControlsManager::ScalarRightPedal]
|
|
.Add(RPL4ModeManager::ModeAlwaysActive, &rightPedal, this);
|
|
|
|
controls
|
|
->buttonGroup[LBE4ControlsManager::ButtonJoystickHatRight]
|
|
.Add(RPL4ModeManager::ModeAlwaysActive, &lookRight, this);
|
|
|
|
controls
|
|
->buttonGroup[LBE4ControlsManager::ButtonJoystickHatLeft]
|
|
.Add(RPL4ModeManager::ModeAlwaysActive, &lookLeft, this);
|
|
|
|
controls
|
|
->buttonGroup[LBE4ControlsManager::ButtonJoystickHatUp]
|
|
.Add(RPL4ModeManager::ModeAlwaysActive, &lookUp, this);
|
|
|
|
controls
|
|
->buttonGroup[LBE4ControlsManager::ButtonJoystickHatDown]
|
|
.Add(RPL4ModeManager::ModeAlwaysActive, &lookBehind, this);
|
|
|
|
controls
|
|
->buttonGroup[LBE4ControlsManager::ButtonThrottle1]
|
|
.Add(RPL4ModeManager::ModeAlwaysActive, &reverseThrust, this);
|
|
|
|
controls
|
|
->keyboardGroup[LBE4ControlsManager::KeyboardPC]
|
|
.Add(RPL4ModeManager::ModeAlwaysActive, this, KeypressMessageID, this);
|
|
|
|
Check_Fpu();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
VTVThrustmasterMapper::~VTVThrustmasterMapper()
|
|
{
|
|
}
|
|
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
Logical
|
|
VTVThrustmasterMapper::TestInstance() const
|
|
{
|
|
return IsDerivedFrom(*GetClassDerivations());
|
|
}
|
|
|
|
//#############################################################################
|
|
//################################ RIOMapper ##################################
|
|
//#############################################################################
|
|
|
|
//#############################################################################
|
|
// Shared Data Support
|
|
//
|
|
VTVRIOMapper::SharedData
|
|
VTVRIOMapper::DefaultData(
|
|
VTVRIOMapper::GetClassDerivations(),
|
|
VTVRIOMapper::MessageHandlers,
|
|
VTVRIOMapper::GetAttributeIndex(),
|
|
VTVRIOMapper::StateCount
|
|
);
|
|
|
|
Derivation* VTVRIOMapper::GetClassDerivations()
|
|
{ static Derivation classDerivations(L4VTVControlsMapper::GetClassDerivations(), "VTVRIOMapper");
|
|
return &classDerivations;
|
|
}
|
|
|
|
|
|
//#############################################################################
|
|
// Messaging Support
|
|
//
|
|
const Receiver::HandlerEntry
|
|
VTVRIOMapper::MessageHandlerEntries[]=
|
|
{
|
|
MESSAGE_ENTRY(VTVRIOMapper, Keypress),
|
|
MESSAGE_ENTRY(VTVRIOMapper, SwitchMode),
|
|
MESSAGE_ENTRY(VTVRIOMapper, SelectPreset),
|
|
MESSAGE_ENTRY(VTVRIOMapper, CenterJoystick),
|
|
MESSAGE_ENTRY(VTVRIOMapper, ConfigureControls)
|
|
};
|
|
|
|
Receiver::MessageHandlerSet
|
|
VTVRIOMapper::MessageHandlers(
|
|
ELEMENTS(VTVRIOMapper::MessageHandlerEntries),
|
|
VTVRIOMapper::MessageHandlerEntries,
|
|
L4VTVControlsMapper::GetMessageHandlers()
|
|
);
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
VTVRIOMapper::KeypressMessageHandler(
|
|
ReceiverDataMessageOf<ControlsKey> *message
|
|
)
|
|
{
|
|
Check(this);
|
|
Check(message);
|
|
|
|
switch (message->dataContents)
|
|
{
|
|
case PCK_F1:
|
|
{
|
|
Check(application);
|
|
LBE4ControlsManager *controls = Cast_Object(
|
|
LBE4ControlsManager*,
|
|
application->GetControlsManager()
|
|
);
|
|
Check(controls);
|
|
|
|
Tell("Centered RIO Joystick\n");
|
|
controls->CenterRIOJoystick();
|
|
}
|
|
break;
|
|
|
|
case PCK_F12:
|
|
Check(application);
|
|
if (
|
|
application->GetApplicationState() != Application::EndingMission
|
|
&& application->GetApplicationState() != Application::StoppingMission
|
|
)
|
|
{
|
|
DEBUG_STREAM << "Keystroke invoking mission stop...\n" << std::flush;
|
|
Exit_Code = 1;
|
|
Application::StopMissionMessage message(Exit_Code);
|
|
application->Dispatch(&message);
|
|
}
|
|
break;
|
|
|
|
case 'W':
|
|
case 'w':
|
|
{
|
|
ReceiverDataMessageOf<ControlsButton>
|
|
config_msg(
|
|
Booster::ActivateMessageID,
|
|
sizeof(ReceiverDataMessageOf<ControlsButton>),
|
|
1
|
|
);
|
|
for (int i=0; i < GetEntity()->GetSubsystemCount(); i++)
|
|
{
|
|
Subsystem *sub = GetEntity()->GetSubsystem(i);
|
|
if (sub && sub->GetClassID() == VTV::BoosterClassID)
|
|
{
|
|
sub->Dispatch(&config_msg);
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
|
|
//-------------------------------------------------------
|
|
// Control eyepoint views
|
|
//-------------------------------------------------------
|
|
case PCK_UP: // look forward
|
|
lookLeft = -1;
|
|
lookRight = -1;
|
|
lookBehind = -1;
|
|
lookUp = -1;
|
|
break;
|
|
case PCK_LEFT: // look left
|
|
lookLeft = 1;
|
|
lookRight = -1;
|
|
lookBehind = -1;
|
|
lookUp = -1;
|
|
break;
|
|
case PCK_RIGHT: // look right
|
|
lookLeft = -1;
|
|
lookRight = 1;
|
|
lookBehind = -1;
|
|
lookUp = -1;
|
|
break;
|
|
case PCK_DOWN: // look behind
|
|
lookLeft = -1;
|
|
lookRight = -1;
|
|
lookBehind = 1;
|
|
lookUp = -1;
|
|
break;
|
|
case PCK_CTL_UP: // look up
|
|
lookLeft = -1;
|
|
lookRight = -1;
|
|
lookBehind = -1;
|
|
lookUp = 1;
|
|
break;
|
|
|
|
default:
|
|
L4VTVControlsMapper::KeypressMessageHandler(message);
|
|
break;
|
|
}
|
|
Check_Fpu();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
VTVRIOMapper::SwitchModeMessageHandler(
|
|
ReceiverDataMessageOf<ControlsButton> *message
|
|
)
|
|
{
|
|
Check(this);
|
|
|
|
//-------------------------------------------
|
|
// Switch on 'press' only (ignore release)
|
|
//-------------------------------------------
|
|
if (message->dataContents > 0)
|
|
{
|
|
//----------------------------------
|
|
// Pick the selected mode
|
|
//----------------------------------
|
|
switch (message->dataContents - 1)
|
|
{
|
|
case LBE4ControlsManager::ButtonAuxUpperRight5:
|
|
SetControlsMode(BasicMode);
|
|
break;
|
|
|
|
case LBE4ControlsManager::ButtonAuxUpperRight6:
|
|
SetControlsMode(StandardMode);
|
|
break;
|
|
|
|
case LBE4ControlsManager::ButtonAuxUpperRight7:
|
|
SetControlsMode(VeteranMode);
|
|
break;
|
|
|
|
case LBE4ControlsManager::ButtonAuxUpperRight8:
|
|
SetControlsMode(MasterMode);
|
|
break;
|
|
}
|
|
}
|
|
Check_Fpu();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
VTVRIOMapper::SelectPresetMessageHandler(
|
|
ReceiverDataMessageOf<ControlsButton> *message
|
|
)
|
|
{
|
|
Check(this);
|
|
//-------------------------------------------
|
|
// Switch on 'press' only (ignore release)
|
|
//-------------------------------------------
|
|
if (message->dataContents > 0)
|
|
{
|
|
//-----------------------------------
|
|
// Choose a new preset. PresetEnable
|
|
// ignores a repeat of the lit switch
|
|
// and moves the lamps itself.
|
|
//-----------------------------------
|
|
PresetEnable(
|
|
(message->dataContents - 1) - LBE4ControlsManager::ButtonSecondary7
|
|
);
|
|
}
|
|
Check_Fpu();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
VTVRIOMapper::CenterJoystickMessageHandler(
|
|
ReceiverDataMessageOf<ControlsButton> *message
|
|
)
|
|
{
|
|
Check(this);
|
|
if (message->dataContents > 0)
|
|
{
|
|
Check(application);
|
|
LBE4ControlsManager
|
|
*controls = (LBE4ControlsManager *) application->GetControlsManager();
|
|
Check(controls);
|
|
|
|
controls->CenterRIOJoystick();
|
|
}
|
|
Check_Fpu();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
VTVRIOMapper::ConfigureControlsMessageHandler(
|
|
ReceiverDataMessageOf<ControlsButton> *message
|
|
)
|
|
{
|
|
Check(this);
|
|
|
|
//-------------------------------------------------------------
|
|
// Allow the VTVControlsMapper to process the message
|
|
//-------------------------------------------------------------
|
|
|
|
// OBSOLETE
|
|
// VTVControlsMapper::ConfigureControlsMessageHandler(message);
|
|
//-------------------------------------------
|
|
// Switch on 'press' only (ignore release)
|
|
//-------------------------------------------
|
|
if (message->dataContents > 0)
|
|
{
|
|
//-------------------------------------------
|
|
// Modify mode
|
|
//-------------------------------------------
|
|
if (GetSimulationState() == ConfigurationState)
|
|
{
|
|
SetConfigurationMode(False);
|
|
}
|
|
else
|
|
{
|
|
SetConfigurationMode(True);
|
|
}
|
|
}
|
|
Check_Fpu();
|
|
}
|
|
|
|
//#############################################################################
|
|
// Model Support
|
|
//
|
|
void
|
|
VTVRIOMapper::InterpretControls(Scalar time_slice)
|
|
{
|
|
//
|
|
//----------------------------------------
|
|
// Reverse if throttle button held in
|
|
//----------------------------------------
|
|
//
|
|
if (throttleReverse > 0)
|
|
{
|
|
reverseThrust = 1;
|
|
throttleReverse = 0;
|
|
}
|
|
else if (throttleReverse < 0)
|
|
{
|
|
reverseThrust = 0;
|
|
throttleReverse = 0;
|
|
}
|
|
//
|
|
//----------------------------------------
|
|
// Sum the pedal inputs
|
|
//----------------------------------------
|
|
//
|
|
pedalsPosition = leftPedal - rightPedal;
|
|
|
|
VTVControlsMapper::InterpretControls(time_slice);
|
|
Check_Fpu();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
VTVRIOMapper::AddOrErase(
|
|
unsigned int button_ID,
|
|
Receiver *target,
|
|
Receiver::MessageID message_ID
|
|
)
|
|
{
|
|
Check(this);
|
|
Check(target);
|
|
|
|
Check(application);
|
|
LBE4ControlsManager *controls =
|
|
Cast_Object(LBE4ControlsManager*,application->GetControlsManager());
|
|
Check(controls);
|
|
|
|
controls->buttonGroup[button_ID-1]
|
|
.AddOrErase(previousPresetModeMask, target, message_ID, this);
|
|
Check_Fpu();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
VTVRIOMapper::AddOrErase(
|
|
unsigned int button_ID,
|
|
ControlsButton *destination
|
|
)
|
|
{
|
|
Check(this);
|
|
Check_Pointer(destination);
|
|
|
|
Check(application);
|
|
LBE4ControlsManager *controls =
|
|
Cast_Object(LBE4ControlsManager*,application->GetControlsManager());
|
|
Check(controls);
|
|
|
|
controls->buttonGroup[button_ID-1]
|
|
.AddOrErase(previousPresetModeMask, destination, this);
|
|
Check_Fpu();
|
|
}
|
|
|
|
//#############################################################################
|
|
// Platform-specific updates
|
|
//
|
|
void
|
|
VTVRIOMapper::NotifyOfControlModeChange(int new_mode)
|
|
{
|
|
Check(this);
|
|
|
|
int
|
|
lamp_number;
|
|
|
|
//----------------------------------
|
|
// Set the old mode button to 'dim'
|
|
//----------------------------------
|
|
switch (previousControlMode)
|
|
{
|
|
case BasicMode: lamp_number = 0; break;
|
|
case StandardMode: lamp_number = 1; break;
|
|
case VeteranMode: lamp_number = 2; break;
|
|
case MasterMode: lamp_number = 3; break;
|
|
default: lamp_number = -1; break;
|
|
}
|
|
|
|
if (lamp_number >= 0)
|
|
{
|
|
Verify(lamp_number < modeLampCount);
|
|
if (modeLamp[lamp_number] != NULL)
|
|
{
|
|
Check(modeLamp[lamp_number]);
|
|
modeLamp[lamp_number]->SetState(L4Lamp::LampStateDim);
|
|
}
|
|
}
|
|
|
|
//----------------------------------
|
|
// Pick the selected mode
|
|
//----------------------------------
|
|
switch (controlMode)
|
|
{
|
|
case BasicMode: lamp_number = 0; break;
|
|
case StandardMode: lamp_number = 1; break;
|
|
case VeteranMode: lamp_number = 2; break;
|
|
case MasterMode: lamp_number = 3; break;
|
|
default: lamp_number = -1; break;
|
|
}
|
|
if (lamp_number >= 0)
|
|
{
|
|
Verify(lamp_number < modeLampCount);
|
|
if (modeLamp[lamp_number] != NULL)
|
|
{
|
|
Check(modeLamp[lamp_number]);
|
|
modeLamp[lamp_number]->SetState(L4Lamp::LampStateOn);
|
|
}
|
|
}
|
|
//-----------------------------------
|
|
// Invoke ancestral method
|
|
//-----------------------------------
|
|
L4VTVControlsMapper::NotifyOfControlModeChange(new_mode);
|
|
|
|
Check_Fpu();
|
|
}
|
|
|
|
void
|
|
VTVRIOMapper::NotifyOfConfigurationModeChange(Logical new_state)
|
|
{
|
|
Check(this);
|
|
|
|
for(int i=0; i<configLampCount; ++i)
|
|
{
|
|
if (configLamp[i] != NULL)
|
|
{
|
|
Check(configLamp[i]);
|
|
configLamp[i]->SetAlertState(new_state);
|
|
}
|
|
}
|
|
//-----------------------------------
|
|
// Invoke ancestral method
|
|
//-----------------------------------
|
|
L4VTVControlsMapper::NotifyOfConfigurationModeChange(new_state);
|
|
|
|
Check_Fpu();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// The six amber switches down the map's right flank. Called by PresetEnable,
|
|
// so the lamps follow the mappings no matter what asked for the change.
|
|
//
|
|
void
|
|
VTVRIOMapper::NotifyOfPresetChange(int old_preset, int new_preset)
|
|
{
|
|
Check(this);
|
|
|
|
//----------------------------------
|
|
// Set the old preset lamp to 'dim'
|
|
//----------------------------------
|
|
if (old_preset >= 0)
|
|
{
|
|
Verify(old_preset < presetCount);
|
|
|
|
if (presetLamp[old_preset] != NULL)
|
|
{
|
|
Check(presetLamp[old_preset]);
|
|
presetLamp[old_preset]->SetState(L4Lamp::LampStateDim);
|
|
}
|
|
}
|
|
//----------------------------------
|
|
// Set the new preset lamp to 'on'
|
|
//----------------------------------
|
|
if (new_preset >= 0)
|
|
{
|
|
Verify(new_preset < presetCount);
|
|
|
|
if (presetLamp[new_preset] != NULL)
|
|
{
|
|
Check(presetLamp[new_preset]);
|
|
presetLamp[new_preset]->SetState(L4Lamp::LampStateOn);
|
|
}
|
|
}
|
|
Check_Fpu();
|
|
}
|
|
|
|
//#############################################################################
|
|
// Construction and Destruction Support
|
|
//
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
VTVRIOMapper::VTVRIOMapper(
|
|
VTV *player_vehicle,
|
|
int subsystem_ID,
|
|
SubsystemResource *subsystem_resource
|
|
):
|
|
L4VTVControlsMapper(
|
|
player_vehicle,
|
|
subsystem_ID,
|
|
subsystem_resource,
|
|
DefaultData
|
|
)
|
|
{
|
|
SetPerformance(&VTVRIOMapper::InterpretControls);
|
|
throttleUp = 0;
|
|
throttleDown = 0;
|
|
throttleReverse = 0;
|
|
leftPedal = 0.0f;
|
|
rightPedal = 0.0f;
|
|
|
|
//------------------------------------------------
|
|
// There are no lamps until the mapping blocks
|
|
// below make them - and under NOMODES they never
|
|
// do, so the notify methods must see NULLs.
|
|
//------------------------------------------------
|
|
{
|
|
int
|
|
i;
|
|
|
|
for(i=0; i<configLampCount; ++i) configLamp[i] = NULL;
|
|
for(i=0; i<modeLampCount; ++i) modeLamp[i] = NULL;
|
|
for(i=0; i<presetCount; ++i) presetLamp[i] = NULL;
|
|
}
|
|
|
|
Check(application);
|
|
LBE4ControlsManager
|
|
*controls = Cast_Object(
|
|
LBE4ControlsManager*,
|
|
application->GetControlsManager()
|
|
);
|
|
Check(controls);
|
|
|
|
VTV
|
|
*vtv = GetEntity();
|
|
Check(vtv);
|
|
|
|
//
|
|
//------------------
|
|
// Throttle mappings
|
|
//------------------
|
|
//
|
|
controls
|
|
->scalarGroup[LBE4ControlsManager::ScalarThrottle]
|
|
.Add(RPL4ModeManager::ModeAlwaysActive, &throttlePosition, this);
|
|
controls
|
|
->buttonGroup[LBE4ControlsManager::ButtonThrottle1]
|
|
.Add(RPL4ModeManager::ModeAlwaysActive, &reverseThrust, this);
|
|
|
|
//
|
|
//------------------
|
|
// Joystick mappings
|
|
//------------------
|
|
//
|
|
controls
|
|
->joystickGroup[LBE4ControlsManager::JoystickPhysical]
|
|
.Add(RPL4ModeManager::ModeAlwaysActive, &stickPosition, this);
|
|
//
|
|
//------------------
|
|
// Eyepoint mappings
|
|
//------------------
|
|
//
|
|
controls
|
|
->buttonGroup[LBE4ControlsManager::ButtonJoystickHatLeft]
|
|
.Add(RPL4ModeManager::ModeAlwaysActive, &lookLeft, this);
|
|
controls
|
|
->buttonGroup[LBE4ControlsManager::ButtonJoystickHatRight]
|
|
.Add(RPL4ModeManager::ModeAlwaysActive, &lookRight, this);
|
|
controls
|
|
->buttonGroup[LBE4ControlsManager::ButtonJoystickHatUp]
|
|
.Add(RPL4ModeManager::ModeAlwaysActive, &lookUp, this);
|
|
controls
|
|
->buttonGroup[LBE4ControlsManager::ButtonJoystickHatDown]
|
|
.Add(RPL4ModeManager::ModeAlwaysActive, &lookBehind, this);
|
|
//
|
|
//--------------
|
|
// Pedal mapping
|
|
//--------------
|
|
//
|
|
controls
|
|
->scalarGroup[LBE4ControlsManager::ScalarLeftPedal]
|
|
.Add(RPL4ModeManager::ModeAlwaysActive, &leftPedal, this);
|
|
|
|
controls
|
|
->scalarGroup[LBE4ControlsManager::ScalarRightPedal]
|
|
.Add(RPL4ModeManager::ModeAlwaysActive, &rightPedal, this);
|
|
|
|
//
|
|
//-----------------
|
|
// Keyboard mapping
|
|
//-----------------
|
|
//
|
|
controls
|
|
->keyboardGroup[LBE4ControlsManager::KeyboardPC]
|
|
.Add(RPL4ModeManager::ModeAlwaysActive, this, KeypressMessageID, this);
|
|
|
|
//----------------------------------------------------------------
|
|
// Configuration mapping
|
|
//----------------------------------------------------------------
|
|
|
|
// Lamps explicitly controlled by ConfigureControlsMessageHandler
|
|
|
|
configLamp[0] = CreateControlledLamp(
|
|
LBE4ControlsManager::ButtonAuxUpperRight1,
|
|
RPL4ModeManager::ModeStandard
|
|
);
|
|
|
|
configLamp[1] = CreateControlledLamp(
|
|
LBE4ControlsManager::ButtonAuxUpperRight2,
|
|
RPL4ModeManager::ModeStandard
|
|
);
|
|
|
|
|
|
if (!getenv("NOMODES"))
|
|
{
|
|
int
|
|
i;
|
|
|
|
for(i=0; i<configLampCount; ++i)
|
|
{
|
|
if (configLamp[i] != NULL)
|
|
{
|
|
Check(configLamp[i]);
|
|
configLamp[i]->SetState(L4Lamp::LampStateDim);
|
|
}
|
|
}
|
|
|
|
controls
|
|
->buttonGroup[LBE4ControlsManager::ButtonAuxUpperRight1]
|
|
.Add(
|
|
RPL4ModeManager::ModeStandard,
|
|
this,
|
|
ConfigureControlsMessageID, this
|
|
);
|
|
|
|
controls
|
|
->buttonGroup[LBE4ControlsManager::ButtonAuxUpperRight2]
|
|
.Add(
|
|
RPL4ModeManager::ModeStandard,
|
|
this,
|
|
ConfigureControlsMessageID, this
|
|
);
|
|
}
|
|
|
|
//----------------------------------------------------------------
|
|
// Joystick centering
|
|
//----------------------------------------------------------------
|
|
MakeButtonLampEventPair(
|
|
controls,
|
|
LBE4ControlsManager::ButtonAuxUpperRight3,
|
|
LBE4ControlsManager::ButtonAuxUpperRight4,
|
|
this,
|
|
CenterJoystickMessageID,
|
|
this
|
|
);
|
|
|
|
//----------------------------------------------------------------
|
|
// Secondary mappings
|
|
//----------------------------------------------------------------
|
|
MakeButtonLampEvent(
|
|
controls,
|
|
LBE4ControlsManager::ButtonSecondary1,
|
|
vtv,
|
|
VTV::ZoomInMessageID,
|
|
this
|
|
);
|
|
|
|
MakeButtonLampEvent(
|
|
controls,
|
|
LBE4ControlsManager::ButtonSecondary2,
|
|
vtv,
|
|
VTV::ZoomOutMessageID,
|
|
this
|
|
);
|
|
|
|
MakeButtonLampEvent(
|
|
controls,
|
|
LBE4ControlsManager::ButtonSecondary3,
|
|
this,
|
|
ToggleReticleMessageID,
|
|
this
|
|
);
|
|
|
|
MakeButtonLampEvent(
|
|
controls,
|
|
LBE4ControlsManager::ButtonSecondary4,
|
|
this,
|
|
ActivateHornMessageID,
|
|
this
|
|
);
|
|
|
|
//----------------------------------------------------------------
|
|
// Mode mapping
|
|
//----------------------------------------------------------------
|
|
previousControlMode = -1;
|
|
|
|
if (!getenv("NOMODES"))
|
|
{
|
|
int
|
|
i;
|
|
|
|
static int
|
|
button_number[modeLampCount] =
|
|
{
|
|
LBE4ControlsManager::ButtonAuxUpperRight5,
|
|
LBE4ControlsManager::ButtonAuxUpperRight6,
|
|
LBE4ControlsManager::ButtonAuxUpperRight7,
|
|
LBE4ControlsManager::ButtonAuxUpperRight8
|
|
};
|
|
|
|
for(i=0; i<modeLampCount; ++i)
|
|
{
|
|
controls->buttonGroup[button_number[i]].Add(
|
|
RPL4ModeManager::ModeAlwaysActive,
|
|
this,
|
|
SwitchModeMessageID,
|
|
this
|
|
);
|
|
|
|
//----------------------------------------------
|
|
// These lamps are explicitly controlled by
|
|
// VTVRIOMapper::NotifyOfControlModeChange
|
|
//----------------------------------------------
|
|
modeLamp[i] = CreateControlledLamp(button_number[i]);
|
|
|
|
if (modeLamp[i] != NULL)
|
|
{
|
|
Check(modeLamp[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------
|
|
// Preset mapping
|
|
//----------------------------------------------------------------
|
|
if (!getenv("NOMODES"))
|
|
{
|
|
int
|
|
i;
|
|
|
|
static int
|
|
button_number[presetCount] =
|
|
{
|
|
LBE4ControlsManager::ButtonSecondary7,
|
|
LBE4ControlsManager::ButtonSecondary8,
|
|
LBE4ControlsManager::ButtonSecondary9,
|
|
LBE4ControlsManager::ButtonSecondary10,
|
|
LBE4ControlsManager::ButtonSecondary11,
|
|
LBE4ControlsManager::ButtonSecondary12
|
|
};
|
|
|
|
for(i=0; i<presetCount; ++i)
|
|
{
|
|
controls->buttonGroup[button_number[i]].Add(
|
|
RPL4ModeManager::ModeAlwaysActive,
|
|
this,
|
|
SelectPresetMessageID,
|
|
this
|
|
);
|
|
|
|
// These lamps are explicitly controlled by NotifyOfPresetChange.
|
|
// They are six, and they are NOT the four mode lamps above.
|
|
presetLamp[i] = CreateControlledLamp(button_number[i]);
|
|
|
|
if (presetLamp[i] != NULL)
|
|
{
|
|
Check(presetLamp[i]);
|
|
presetLamp[i]->SetState(
|
|
(i==0)? L4Lamp::LampStateOn : L4Lamp::LampStateDim
|
|
);
|
|
}
|
|
}
|
|
NotifyOfControlModeChange(BasicMode);
|
|
}
|
|
|
|
//----------------------------------------------------------------
|
|
// liftcut mapping
|
|
//----------------------------------------------------------------
|
|
CreateDirectSystemMappingPair(
|
|
controls,
|
|
LBE4ControlsManager::ButtonAuxUpperLeft1,
|
|
LBE4ControlsManager::ButtonAuxUpperLeft2,
|
|
&liftCut,
|
|
this,
|
|
ConfigureLiftCutMappablesMessageID
|
|
);
|
|
//----------------------------------------------------------------
|
|
// sideslip mapping
|
|
//----------------------------------------------------------------
|
|
CreateDirectSystemMappingPair(
|
|
controls,
|
|
LBE4ControlsManager::ButtonAuxUpperLeft3,
|
|
LBE4ControlsManager::ButtonAuxUpperLeft4,
|
|
&sideSlip,
|
|
this,
|
|
ConfigureSideSlipMappablesMessageID
|
|
);
|
|
//----------------------------------------------------------------
|
|
// intercom PTT mapping
|
|
//----------------------------------------------------------------
|
|
CreateEventSystemMappingPair(
|
|
controls,
|
|
LBE4ControlsManager::ButtonAuxUpperLeft5,
|
|
LBE4ControlsManager::ButtonAuxUpperLeft6,
|
|
this,
|
|
ActivatePTTMessageID,
|
|
ConfigurePTTMappablesMessageID
|
|
);
|
|
//----------------------------------------------------------------
|
|
// horn mapping
|
|
//----------------------------------------------------------------
|
|
CreateEventSystemMappingPair(
|
|
controls,
|
|
LBE4ControlsManager::ButtonAuxUpperLeft7,
|
|
LBE4ControlsManager::ButtonAuxUpperLeft8,
|
|
this,
|
|
ActivateHornMessageID,
|
|
ConfigureHornMappablesMessageID
|
|
);
|
|
|
|
Check_Fpu();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
VTVRIOMapper::~VTVRIOMapper()
|
|
{
|
|
Check(this);
|
|
|
|
// HACK - gaugeRenderer::ShutdownImplementation() removes all lamps.
|
|
// When this destructor gets called, the lamps have (usually) already
|
|
// been deleted. The fix is to have all lamp operations go through
|
|
// the lamp manager, referring to the lamp by ID and modeMask...then,
|
|
// when a request is made for a nonexistent lamp, the request is ignored.
|
|
|
|
#if 0
|
|
|
|
int
|
|
i;
|
|
|
|
//-----------------------------------------------
|
|
// First check to see if the lamp manager exists.
|
|
// If it doesn't, then the lamps have either
|
|
// not been created or else have already
|
|
// been deleted.
|
|
//-----------------------------------------------
|
|
Check(application);
|
|
if (application->GetGaugeRenderer() != NULL)
|
|
{
|
|
Check(application->GetGaugeRenderer());
|
|
|
|
if (application->GetGaugeRenderer()->GetLampManager() != NULL)
|
|
{
|
|
Check(application->GetGaugeRenderer()->GetLampManager());
|
|
//--------------------------------------------
|
|
// Delete controlled lamp objects
|
|
//--------------------------------------------
|
|
for(i=0; i<configLampCount; ++i)
|
|
{
|
|
if (configLamp[i] != NULL)
|
|
{
|
|
Unregister_Object(configLamp[i]);
|
|
delete configLamp[i];
|
|
configLamp[i] = NULL;
|
|
}
|
|
}
|
|
|
|
for(i=0; i<modeLampCount; ++i)
|
|
{
|
|
if (modeLamp[i] != NULL)
|
|
{
|
|
Unregister_Object(modeLamp[i]);
|
|
delete modeLamp[i];
|
|
modeLamp[i] = NULL;
|
|
}
|
|
}
|
|
|
|
for(i=0; i<presetCount; ++i)
|
|
{
|
|
if (presetLamp[i] != NULL)
|
|
{
|
|
Unregister_Object(presetLamp[i]);
|
|
delete presetLamp[i];
|
|
presetLamp[i] = NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
Check_Fpu();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
Logical
|
|
VTVRIOMapper::TestInstance() const
|
|
{
|
|
return IsDerivedFrom(*GetClassDerivations());
|
|
}
|