Imports the current Win32 source for the pod-racing game 'Red Planet', built on the MUNGA engine and its L4 (Win32/DirectX) platform layer: - MUNGA / MUNGA_L4: cross-platform engine core and Win32 backend - RP / RP_L4: Red Planet game logic and Win32 application - DivLoader, Setup1: asset loader and installer project - lib, MUNGA_L4/openal, MUNGA_L4/sos: third-party audio dependencies Removed stale Subversion metadata and added .gitignore/.gitattributes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
959 lines
25 KiB
C++
959 lines
25 KiB
C++
#include "rp.h"
|
|
#pragma hdrstop
|
|
|
|
#include "vtvmppr.h"
|
|
#include "vtvpwr.h"
|
|
#include "..\munga\icom.h"
|
|
#include "..\munga\app.h"
|
|
#include "rpplayer.h"
|
|
#include "vtv.h"
|
|
|
|
//#############################################################################
|
|
// Shared Data Support
|
|
//
|
|
VTVControlsMapper::SharedData
|
|
VTVControlsMapper::DefaultData(
|
|
VTVControlsMapper::GetClassDerivations(),
|
|
VTVControlsMapper::GetMessageHandlers(),
|
|
VTVControlsMapper::GetAttributeIndex(),
|
|
VTVControlsMapper::StateCount
|
|
);
|
|
|
|
Derivation* VTVControlsMapper::GetClassDerivations()
|
|
{
|
|
static Derivation classDerivations(VTVSubsystem::GetClassDerivations(), "VTVControlsMapper");
|
|
return &classDerivations;
|
|
}
|
|
|
|
//#############################################################################
|
|
// Messaging Support
|
|
//
|
|
const Receiver::HandlerEntry
|
|
VTVControlsMapper::MessageHandlerEntries[]=
|
|
{
|
|
MESSAGE_ENTRY(VTVControlsMapper, ConfigureControls),
|
|
MESSAGE_ENTRY(VTVControlsMapper, ConfigureSideSlipMappables),
|
|
MESSAGE_ENTRY(VTVControlsMapper, ChooseSideSlip),
|
|
MESSAGE_ENTRY(VTVControlsMapper, ConfigureLiftCutMappables),
|
|
MESSAGE_ENTRY(VTVControlsMapper, ChooseLiftCut),
|
|
MESSAGE_ENTRY(VTVControlsMapper, ConfigureHornMappables),
|
|
MESSAGE_ENTRY(VTVControlsMapper, ChooseHorn),
|
|
MESSAGE_ENTRY(VTVControlsMapper, ActivateHorn),
|
|
MESSAGE_ENTRY(VTVControlsMapper, ConfigurePTTMappables),
|
|
MESSAGE_ENTRY(VTVControlsMapper, ChoosePTT),
|
|
MESSAGE_ENTRY(VTVControlsMapper, ActivatePTT),
|
|
MESSAGE_ENTRY(VTVControlsMapper, ToggleReticle)
|
|
};
|
|
|
|
Receiver::MessageHandlerSet& VTVControlsMapper::GetMessageHandlers()
|
|
{
|
|
static Receiver::MessageHandlerSet messageHandlers(ELEMENTS(VTVControlsMapper::MessageHandlerEntries), VTVControlsMapper::MessageHandlerEntries, VTVSubsystem::GetMessageHandlers());
|
|
return messageHandlers;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
VTVControlsMapper::ConfigureControlsMessageHandler(
|
|
#if DEBUG_LEVEL>0
|
|
ReceiverDataMessageOf<ControlsButton> *message
|
|
#else
|
|
ReceiverDataMessageOf<ControlsButton> *
|
|
#endif
|
|
)
|
|
{
|
|
Check(this);
|
|
Check(message);
|
|
|
|
#if 0
|
|
THIS STUFF IS BASICALLY OBSOLETE
|
|
//
|
|
//------------------------
|
|
// Get the control manager
|
|
//------------------------
|
|
//
|
|
Check(application);
|
|
ControlsManager *control = application->GetControlsManager();
|
|
Check(control);
|
|
|
|
//
|
|
//----------------------------------------------------------------------
|
|
// If the config button was pressed, set all hardwired buttons to config
|
|
// mode, otherwise set them all back to active mode, and clear out any
|
|
// temporary mappings to the mappable buttons
|
|
//----------------------------------------------------------------------
|
|
//
|
|
if (message->dataContents > 0)
|
|
{
|
|
if (GetSimulationState() == ConfigurationState)
|
|
{
|
|
control->SetHardwiredButtonsFilter(ActiveFilter, 0);
|
|
control->StopMappableButtonsConfigure(
|
|
ConfigurationFilter,
|
|
mustMatch,
|
|
mayMatch
|
|
);
|
|
SetSimulationState(DefaultState);
|
|
}
|
|
else
|
|
{
|
|
if (controlMode != BasicMode)
|
|
{
|
|
control->SetHardwiredButtonsFilter(ConfigurationFilter, 0);
|
|
SetSimulationState(ConfigurationState);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
Check_Fpu();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
VTVControlsMapper::ConfigureSideSlipMappablesMessageHandler(
|
|
ReceiverDataMessageOf<ControlsButton> *message
|
|
)
|
|
{
|
|
Check(this);
|
|
Check(message);
|
|
|
|
//---------------------------------------------------------------------
|
|
// If the hardwired button was pressed, process presses of the mappable
|
|
// buttons, otherwise it was released, so erase any temporary mappings
|
|
//---------------------------------------------------------------------
|
|
if (message->dataContents > 0)
|
|
{
|
|
EnterConfiguration(
|
|
&sideSlip, // direct target
|
|
this, // receiver
|
|
(Receiver::MessageID) 0, // activation message ID (none here)
|
|
ChooseSideSlipMessageID // configuration message ID
|
|
);
|
|
}
|
|
else
|
|
{
|
|
ExitConfiguration();
|
|
}
|
|
Check_Fpu();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
VTVControlsMapper::ChooseSideSlipMessageHandler(
|
|
ReceiverDataMessageOf<ControlsButton> *message
|
|
)
|
|
{
|
|
Check(this);
|
|
Check(message);
|
|
|
|
if (message->dataContents > 0)
|
|
{
|
|
AddOrErase(message->dataContents, &sideSlip);
|
|
}
|
|
Check_Fpu();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
VTVControlsMapper::ConfigureLiftCutMappablesMessageHandler(
|
|
ReceiverDataMessageOf<ControlsButton> *message
|
|
)
|
|
{
|
|
Check(this);
|
|
Check(message);
|
|
|
|
//
|
|
//---------------------------------------------------------------------
|
|
// If the hardwired button was pressed, process presses of the mappable
|
|
// buttons, otherwise it was released, so erase any temporary mappings
|
|
//---------------------------------------------------------------------
|
|
//
|
|
if (message->dataContents > 0)
|
|
{
|
|
EnterConfiguration(
|
|
&liftCut, // direct target
|
|
this, // receiver
|
|
(Receiver::MessageID) 0, // activation message ID (none here)
|
|
ChooseLiftCutMessageID // configuration message ID
|
|
);
|
|
}
|
|
else
|
|
{
|
|
ExitConfiguration();
|
|
}
|
|
Check_Fpu();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
VTVControlsMapper::ChooseLiftCutMessageHandler(
|
|
ReceiverDataMessageOf<ControlsButton> *message
|
|
)
|
|
{
|
|
Check(this);
|
|
Check(message);
|
|
|
|
if (message->dataContents > 0)
|
|
{
|
|
AddOrErase(message->dataContents, &liftCut);
|
|
}
|
|
Check_Fpu();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
VTVControlsMapper::ConfigureHornMappablesMessageHandler(
|
|
ReceiverDataMessageOf<ControlsButton> *message
|
|
)
|
|
{
|
|
Check(this);
|
|
Check(message);
|
|
|
|
//
|
|
//---------------------------------------------------------------------
|
|
// If the hardwired button was pressed, process presses of the mappable
|
|
// buttons, otherwise it was released, so erase any temporary mappings
|
|
//---------------------------------------------------------------------
|
|
//
|
|
|
|
if (message->dataContents > 0)
|
|
{
|
|
EnterConfiguration(
|
|
NULL, // direct target (none here)
|
|
this, // receiver
|
|
ActivateHornMessageID, // activation message ID
|
|
ChooseHornMessageID // configuration message ID
|
|
);
|
|
}
|
|
else
|
|
{
|
|
ExitConfiguration();
|
|
}
|
|
Check_Fpu();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
VTVControlsMapper::ChooseHornMessageHandler(
|
|
ReceiverDataMessageOf<ControlsButton> *message
|
|
)
|
|
{
|
|
Check(this);
|
|
Check(message);
|
|
|
|
if (message->dataContents > 0)
|
|
{
|
|
AddOrErase(message->dataContents, this, ActivateHornMessageID);
|
|
}
|
|
Check_Fpu();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
VTVControlsMapper::ActivateHornMessageHandler(
|
|
ReceiverDataMessageOf<ControlsButton> *message
|
|
)
|
|
{
|
|
Check(this);
|
|
Check(message);
|
|
|
|
hornBlast = message->dataContents;
|
|
// hornBlast = (hornBlast == -1) ? 1 : -1;
|
|
|
|
// HACK - ECH 7/31/95 - Propagate horn blast
|
|
Check(GetEntity());
|
|
GetEntity()->hornBlast = hornBlast;
|
|
GetEntity()->ForceUpdate();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
VTVControlsMapper::ConfigurePTTMappablesMessageHandler(
|
|
ReceiverDataMessageOf<ControlsButton> *message
|
|
)
|
|
{
|
|
Check(this);
|
|
Check(message);
|
|
|
|
//
|
|
//---------------------------------------------------------------------
|
|
// If the hardwired button was pressed, process presses of the mappable
|
|
// buttons, otherwise it was released, so erase any temporary mappings
|
|
//---------------------------------------------------------------------
|
|
//
|
|
if (message->dataContents > 0)
|
|
{
|
|
EnterConfiguration(
|
|
NULL, // direct target (none here)
|
|
this, // receiver
|
|
ActivatePTTMessageID, // activation message ID
|
|
ChoosePTTMessageID // configuration message ID
|
|
);
|
|
}
|
|
else
|
|
{
|
|
ExitConfiguration();
|
|
}
|
|
Check_Fpu();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
VTVControlsMapper::ChoosePTTMessageHandler(
|
|
ReceiverDataMessageOf<ControlsButton> *message
|
|
)
|
|
{
|
|
Check(this);
|
|
Check(message);
|
|
|
|
if (message->dataContents > 0)
|
|
{
|
|
AddOrErase(message->dataContents, &pttStatus);
|
|
}
|
|
Check_Fpu();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
VTVControlsMapper::ActivatePTTMessageHandler(
|
|
ReceiverDataMessageOf<ControlsButton> *message
|
|
)
|
|
{
|
|
Check(this);
|
|
Check(message);
|
|
|
|
pttStatus = message->dataContents;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
VTVControlsMapper::ToggleReticleMessageHandler(
|
|
ReceiverDataMessageOf<ControlsButton> *message
|
|
)
|
|
{
|
|
Check(this);
|
|
Check(message);
|
|
|
|
if (message->dataContents > 0)
|
|
{
|
|
VTV* vtv = GetEntity();
|
|
Check(vtv);
|
|
Reticle *reticle = &vtv->targetReticle;
|
|
Check(reticle);
|
|
reticle->reticleState =
|
|
(Reticle::ReticleState)(reticle->reticleState^Reticle::ReticleOn);
|
|
}
|
|
}
|
|
|
|
//#############################################################################
|
|
// Attribute Support
|
|
//
|
|
const VTVControlsMapper::IndexEntry
|
|
VTVControlsMapper::AttributePointers[]=
|
|
{
|
|
ATTRIBUTE_ENTRY(VTVControlsMapper, StickPosition, stickPosition),
|
|
ATTRIBUTE_ENTRY(VTVControlsMapper, ThrottlePosition, throttlePosition),
|
|
ATTRIBUTE_ENTRY(VTVControlsMapper, PedalsPosition, pedalsPosition),
|
|
ATTRIBUTE_ENTRY(VTVControlsMapper, ReverseThrust, reverseThrust),
|
|
ATTRIBUTE_ENTRY(VTVControlsMapper, LiftCut, liftCut),
|
|
ATTRIBUTE_ENTRY(VTVControlsMapper, SideSlip, sideSlip),
|
|
ATTRIBUTE_ENTRY(VTVControlsMapper, PowerDemand, powerDemand),
|
|
ATTRIBUTE_ENTRY(
|
|
VTVControlsMapper,
|
|
AngularVelocityDemand,
|
|
angularVelocityDemand
|
|
),
|
|
ATTRIBUTE_ENTRY(VTVControlsMapper, BrakeLights, brakeLights),
|
|
ATTRIBUTE_ENTRY(VTVControlsMapper, LookLeft, lookLeft),
|
|
ATTRIBUTE_ENTRY(VTVControlsMapper, LookRight, lookRight),
|
|
ATTRIBUTE_ENTRY(VTVControlsMapper, LookBehind, lookBehind),
|
|
ATTRIBUTE_ENTRY(VTVControlsMapper, LookUp, lookUp),
|
|
ATTRIBUTE_ENTRY(VTVControlsMapper, ControlMode, controlMode),
|
|
ATTRIBUTE_ENTRY(VTVControlsMapper, HornBlast, hornBlast),
|
|
ATTRIBUTE_ENTRY(VTVControlsMapper, MustMatch, mustMatch),
|
|
ATTRIBUTE_ENTRY(VTVControlsMapper, ReverseThrustEngaged,reverseThrustEngaged), // ECH 8/3/95 - HACK - deal with thrust
|
|
ATTRIBUTE_ENTRY(VTVControlsMapper, LiftCutEngaged, liftCutEngaged),
|
|
ATTRIBUTE_ENTRY(VTVControlsMapper, PTTStatus, pttStatus)
|
|
};
|
|
|
|
VTVControlsMapper::AttributeIndexSet& VTVControlsMapper::GetAttributeIndex()
|
|
{
|
|
static VTVControlsMapper::AttributeIndexSet attributeIndex(ELEMENTS(VTVControlsMapper::AttributePointers),
|
|
VTVControlsMapper::AttributePointers,
|
|
VTVSubsystem::GetAttributeIndex()
|
|
);
|
|
return attributeIndex;
|
|
}
|
|
|
|
//#############################################################################
|
|
// Model Support
|
|
//
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
VTVControlsMapper::InterpretControls(Scalar)
|
|
{
|
|
Scalar demand;
|
|
|
|
Check(this);
|
|
|
|
VTV* vtv = GetEntity();
|
|
Check(vtv);
|
|
VTVPower *power_system =
|
|
Cast_Object(VTVPower*, vtv->GetSubsystem(VTV::PowerSubsystem));
|
|
|
|
//
|
|
//----------------------------------------------
|
|
// Make sure the control inputs are within range
|
|
//----------------------------------------------
|
|
//
|
|
Verify(throttlePosition >= 0.0f && throttlePosition <= 1.0f);
|
|
Verify(stickPosition.x >= -1.0f && stickPosition.x <= 1.0f);
|
|
Verify(stickPosition.y >= -1.0f && stickPosition.y <= 1.0f);
|
|
Verify(pedalsPosition >= -1.0f && pedalsPosition <= 1.0f);
|
|
|
|
//
|
|
//----------------------------------------------------------------
|
|
// Figure out the proper control model to use based upon the speed
|
|
//----------------------------------------------------------------
|
|
//
|
|
Scalar lateral_power = power_system->maxAccelerationOutput * ZIPPY;
|
|
powerDemand = Vector3D::Identity;
|
|
int slow_mode =
|
|
fabs(vtv->localVelocity.linearMotion.z) <
|
|
lateral_power / vtv->maxYawVelocity;
|
|
Check_Fpu();
|
|
angularVelocityDemand = Vector3D::Identity;
|
|
Scalar kick_turn = (reverseThrust>0) ? throttlePosition : -throttlePosition;
|
|
powerDemand.z = kick_turn;
|
|
Logical no_steal = False;
|
|
liftCutEngaged = False;
|
|
reverseThrustEngaged = False;
|
|
|
|
//
|
|
//----------------------------------------------------
|
|
// If we are in basic mode, do that autothrottle thing
|
|
//----------------------------------------------------
|
|
//
|
|
if (controlMode == BasicMode)
|
|
{
|
|
Scalar desired_speed =
|
|
powerDemand.z
|
|
* power_system->maxAccelerationOutput
|
|
/ vtv->negativeLinearDragCoefficients.z;
|
|
Check_Fpu();
|
|
Scalar reverse = (reverseThrust>0) ? 1.0f : -1.0f;
|
|
|
|
//
|
|
//-----------------------------------------------------------------------
|
|
// If we have no boosters on, and we are going too fast, and the stick is
|
|
// not pushed near full forward, change the throttle setting
|
|
//-----------------------------------------------------------------------
|
|
//
|
|
if (
|
|
!vtv->BoosterOn()
|
|
&& throttlePosition < 0.1f
|
|
&& desired_speed > vtv->localVelocity.linearMotion.z
|
|
)
|
|
{
|
|
no_steal = True;
|
|
powerDemand.z = -reverse;
|
|
|
|
// ECH 8/3/95 - HACK - deal with thrust
|
|
Scalar temp = Abs(vtv->localVelocity.linearMotion.z) / 60.0f;
|
|
Clamp(temp, 0.0f, 1.0f);
|
|
powerDemand.z *= pow(temp, 0.5f);
|
|
}
|
|
|
|
//
|
|
//--------------------------------------------------------
|
|
// If we are sliding against the engines, go to full power
|
|
//--------------------------------------------------------
|
|
//
|
|
else if (vtv->localVelocity.linearMotion.z * reverse < 0.0f)
|
|
{
|
|
no_steal = True;
|
|
|
|
// ECH 8/3/95 - HACK - deal with thrust
|
|
if (throttlePosition < 0.1f)
|
|
{
|
|
Scalar temp = Abs(vtv->localVelocity.linearMotion.z) / 60.0f;
|
|
Clamp(temp, 0.0f, 1.0f);
|
|
powerDemand.z *= pow(temp, 0.5f);
|
|
}
|
|
}
|
|
}
|
|
brakeLights = (powerDemand.z * vtv->localVelocity.linearMotion.z) < 0.0f;
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// If we are in slow mode, match the lateral power to the turn rate, giving
|
|
// it priority over thrust, so that we will be able to turn a perfect circle
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
if (slow_mode && sideSlip < 1 && controlMode < MasterMode)
|
|
{
|
|
angularVelocityDemand.y += stickPosition.x * vtv->maxYawVelocity;
|
|
powerDemand.x =
|
|
-vtv->maxYawVelocity
|
|
* fabs(vtv->localVelocity.linearMotion.z)
|
|
* stickPosition.x
|
|
/ lateral_power;
|
|
Check_Fpu();
|
|
if (reverseThrust>0 && vtv->localVelocity.linearMotion.z > 0.0f)
|
|
{
|
|
powerDemand.x = -powerDemand.x;
|
|
}
|
|
demand = powerDemand.x*powerDemand.x + powerDemand.z*powerDemand.z;
|
|
Check_Fpu();
|
|
if (demand>1.0f)
|
|
{
|
|
if (powerDemand.z > 0.0f)
|
|
{
|
|
powerDemand.z = Sqrt(1.0f - powerDemand.x*powerDemand.x);
|
|
}
|
|
else
|
|
{
|
|
powerDemand.z = -Sqrt(1.0f - powerDemand.x*powerDemand.x);
|
|
}
|
|
}
|
|
Check_Fpu();
|
|
}
|
|
|
|
//
|
|
//-------------------------------------------------------------------------
|
|
// If we aren't in slow mode, match the turning rate to the power setting
|
|
// on the stick, sharing power equally between forward and sideways demands
|
|
//-------------------------------------------------------------------------
|
|
//
|
|
else
|
|
{
|
|
powerDemand.x = -stickPosition.x;
|
|
demand = powerDemand.x*powerDemand.x + powerDemand.z*powerDemand.z;
|
|
Check_Fpu();
|
|
|
|
//
|
|
//-----------------------------------------------------------------------
|
|
// If we are asking for more power than is available, steal from anything
|
|
// but braking so we can turn quicker if we are in a wimp mode
|
|
//-----------------------------------------------------------------------
|
|
//
|
|
if (demand>1.0f)
|
|
{
|
|
if (controlMode < MasterMode && powerDemand.z < 0.0f && !no_steal)
|
|
{
|
|
powerDemand.z = -Sqrt(1.0f - powerDemand.x*powerDemand.x);
|
|
Check_Fpu();
|
|
}
|
|
else
|
|
{
|
|
demand = Sqrt(demand);
|
|
powerDemand.x /= demand;
|
|
powerDemand.z /= demand;
|
|
Check_Fpu();
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
//-------------------------------------------------------------------------
|
|
// The amount of power required for height as requested by the stick should
|
|
// range between 1m height and the service ceiling
|
|
//-------------------------------------------------------------------------
|
|
//
|
|
JointSubsystem *joint_subsystem = vtv->GetJointSubsystem();
|
|
Check(joint_subsystem);
|
|
Verify(joint_subsystem->GetJointCount());
|
|
|
|
//
|
|
//------------------------------------------------------------------------
|
|
// If the lift cut button is on, go ahead and put all the power on X and Z.
|
|
// In basic mode, if the height is greater than 5m and the stick is pushed
|
|
// mostly forward
|
|
//------------------------------------------------------------------------
|
|
//
|
|
if (
|
|
liftCut > 0
|
|
|| controlMode < VeteranMode
|
|
&& stickPosition.y <= -0.8f
|
|
&& vtv->heightAboveTerrain >= 5.0f
|
|
)
|
|
{
|
|
powerDemand.y = vtv->powerDive;
|
|
if (powerDemand.y)
|
|
{
|
|
Scalar reduction = Sqrt(1.0f - powerDemand.y*powerDemand.y);
|
|
powerDemand.x *= reduction;
|
|
powerDemand.z *= reduction;
|
|
}
|
|
liftCutEngaged = True;
|
|
}
|
|
|
|
//
|
|
//------------------------------------------------------------------------
|
|
// Otherwise, give altitude everything it wants, and split the rest up for
|
|
// X and Z
|
|
//------------------------------------------------------------------------
|
|
//
|
|
else
|
|
{
|
|
if (!vtv->groundEffectDomainSquared)
|
|
{
|
|
if (vtv->heightAboveTerrain >= 2.0f)
|
|
{
|
|
powerDemand.y = 0.0f;
|
|
liftCutEngaged = True;
|
|
}
|
|
else
|
|
{
|
|
demand =
|
|
power_system->maxAccelerationOutput +
|
|
joint_subsystem->GetJointCount() *
|
|
vtv->groundEffectRange;
|
|
powerDemand.y = vtv->GetEnvironment()->gravityConstant/demand;
|
|
}
|
|
}
|
|
else if (controlMode > StandardMode)
|
|
{
|
|
demand =
|
|
power_system->maxAccelerationOutput
|
|
+ joint_subsystem->GetJointCount()
|
|
* (vtv->groundEffectRange
|
|
/ (1.0f + vtv->groundEffectDomainSquared));
|
|
Check_Fpu();
|
|
demand = vtv->GetEnvironment()->gravityConstant/demand;
|
|
powerDemand.y =
|
|
0.5f * ((1.0f - demand) * stickPosition.y + (1.0f + demand));
|
|
Max_Clamp(powerDemand.y, 1.0f);
|
|
Check_Fpu();
|
|
}
|
|
else
|
|
{
|
|
Scalar seek_height = vtv->heightAboveTerrain;
|
|
Scalar height_clamp = 30.0f;
|
|
Max_Clamp(seek_height, height_clamp);
|
|
if (stickPosition.y < 0.0f)
|
|
{
|
|
seek_height =
|
|
::Lerp(seek_height, 1.0f, -stickPosition.y);
|
|
}
|
|
else
|
|
{
|
|
seek_height =
|
|
::Lerp(seek_height, height_clamp, stickPosition.y);
|
|
}
|
|
demand =
|
|
power_system->maxAccelerationOutput +
|
|
joint_subsystem->GetJointCount() *
|
|
(
|
|
vtv->groundEffectRange
|
|
/ (
|
|
1.0f
|
|
+ vtv->groundEffectDomainSquared*seek_height*seek_height
|
|
)
|
|
);
|
|
Check_Fpu();
|
|
powerDemand.y = vtv->GetEnvironment()->gravityConstant/demand;
|
|
if (controlMode == BasicMode)
|
|
{
|
|
Max_Clamp(powerDemand.y, 0.85f);
|
|
}
|
|
else
|
|
{
|
|
Max_Clamp(powerDemand.y, 1.0f);
|
|
}
|
|
Check_Fpu();
|
|
}
|
|
demand = Sqrt(1.0f - powerDemand.y*powerDemand.y);
|
|
powerDemand.x *= demand;
|
|
powerDemand.z *= demand;
|
|
}
|
|
|
|
//
|
|
//----------------------------------------------------------
|
|
// Apply the rudders, and simulate a kick turn in basic mode
|
|
//----------------------------------------------------------
|
|
//
|
|
if (controlMode == BasicMode)
|
|
{
|
|
Scalar temp = stickPosition.x * stickPosition.x;
|
|
temp *= stickPosition.x;
|
|
angularVelocityDemand.y += temp * vtv->maxYawVelocity;
|
|
}
|
|
else if (pedalsPosition < 0.0f)
|
|
{
|
|
angularVelocityDemand.y -=
|
|
vtv->maxYawVelocity * pedalsPosition*pedalsPosition;
|
|
}
|
|
else
|
|
{
|
|
angularVelocityDemand.y +=
|
|
vtv->maxYawVelocity * pedalsPosition*pedalsPosition;
|
|
}
|
|
|
|
//
|
|
//-------------------------------------------------------------------------
|
|
// If we are not is slow mode, apply yaw to the craft to sufficient to make
|
|
// it turn a perfect circle, then clamp to maximum velocity
|
|
//-------------------------------------------------------------------------
|
|
//
|
|
if (!slow_mode && controlMode < MasterMode && sideSlip < 1)
|
|
{
|
|
angularVelocityDemand.y +=
|
|
-powerDemand.x
|
|
* ZIPPY
|
|
* power_system->maxAccelerationOutput
|
|
/ fabs(vtv->localVelocity.linearMotion.z);
|
|
Check_Fpu();
|
|
if (reverseThrust>0 && vtv->localVelocity.linearMotion.z > 0.0f)
|
|
{
|
|
powerDemand.x = -powerDemand.x;
|
|
}
|
|
}
|
|
Clamp(
|
|
angularVelocityDemand.y,
|
|
-vtv->maxYawVelocity,
|
|
vtv->maxYawVelocity
|
|
);
|
|
|
|
//
|
|
//----------------------------------------
|
|
// Turn the eyepoint the direction we want
|
|
//----------------------------------------
|
|
//
|
|
vtv->eyepointRotation = EulerAngles::Identity;
|
|
if (lookLeft > 0)
|
|
{
|
|
vtv->eyepointRotation.yaw = 0.3f * PI;
|
|
}
|
|
else if (lookRight > 0)
|
|
{
|
|
vtv->eyepointRotation.yaw = -0.3f * PI;
|
|
}
|
|
else if (lookBehind > 0)
|
|
{
|
|
vtv->eyepointRotation.yaw = PI;
|
|
}
|
|
else if (lookUp > 0)
|
|
{
|
|
vtv->eyepointRotation.pitch = PI / 8.0f;
|
|
}
|
|
|
|
// ECH - HACK - deal with thrust and lift cut
|
|
averageOfForwardThrustDemand.Add(powerDemand.z);
|
|
forwardThrustDemand = averageOfForwardThrustDemand.CalculateAverage();
|
|
switch (controlMode)
|
|
{
|
|
case BasicMode:
|
|
// Lift cut has been set, see above
|
|
// Reverse thrust if demand over 0.15
|
|
reverseThrustEngaged = (forwardThrustDemand > 0.15f);
|
|
break;
|
|
|
|
case StandardMode:
|
|
// Lift cut has been set, see above
|
|
// Reverse thrust based on button
|
|
reverseThrustEngaged = (reverseThrust > 0);
|
|
break;
|
|
|
|
case VeteranMode:
|
|
case MasterMode:
|
|
// Lift cut based on button
|
|
// Reverse thrust based on button
|
|
liftCutEngaged = (liftCut > 0);
|
|
reverseThrustEngaged = (reverseThrust > 0);
|
|
break;
|
|
}
|
|
Check(GetEntity());
|
|
if (GetEntity()->GetSimulationState() == VTV::BurningState)
|
|
{
|
|
liftCutEngaged = False;
|
|
reverseThrustEngaged = False;
|
|
}
|
|
|
|
//
|
|
//----------------------------------------------------------------
|
|
// Send PTT status to intercom
|
|
//----------------------------------------------------------------
|
|
//
|
|
if (pttStatus != previousPTTStatus)
|
|
{
|
|
previousPTTStatus = pttStatus;
|
|
|
|
Player
|
|
*player_pointer = vtv->GetPlayerLink();
|
|
Check(player_pointer);
|
|
Icom
|
|
*intercom = player_pointer->GetIntercom();
|
|
Check(intercom);
|
|
|
|
intercom->SetPTTStatus((Logical) pttStatus > 0);
|
|
}
|
|
|
|
Check_Fpu();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
VTVControlsMapper::CreateTemporaryEventMappings(
|
|
Receiver */*receiver*/,
|
|
Receiver::MessageID /*config_message_id*/
|
|
)
|
|
{
|
|
Fail("Unhandled mapping!\n");
|
|
}
|
|
|
|
void
|
|
VTVControlsMapper::RemoveTemporaryEventMappings()
|
|
{
|
|
Fail("Unhandled mapping!\n");
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
VTVControlsMapper::AddOrErase(
|
|
unsigned int,
|
|
Receiver *,
|
|
Receiver::MessageID
|
|
)
|
|
{
|
|
Fail("Unhandled mapping!\n");
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
VTVControlsMapper::AddOrErase(
|
|
unsigned int,
|
|
ControlsButton *
|
|
)
|
|
{
|
|
Fail("Unhandled mapping!\n");
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
VTVControlsMapper::SetConfigurationState(Logical enter_config)
|
|
{
|
|
Check(this);
|
|
|
|
if (enter_config)
|
|
{
|
|
if (GetSimulationState() != ConfigurationState)
|
|
{
|
|
SetSimulationState(ConfigurationState);
|
|
NotifyOfConfigurationModeChange(enter_config);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (GetSimulationState() == ConfigurationState)
|
|
{
|
|
SetSimulationState(DefaultState);
|
|
NotifyOfConfigurationModeChange(enter_config);
|
|
}
|
|
}
|
|
Check_Fpu();
|
|
}
|
|
|
|
void
|
|
VTVControlsMapper::NotifyOfControlModeChange(int /*new_mode*/)
|
|
{
|
|
Check(this);
|
|
// defaults to no-operation: intended to be processed by
|
|
// platform-specific derived class
|
|
Check_Fpu();
|
|
}
|
|
|
|
void
|
|
VTVControlsMapper::NotifyOfConfigurationModeChange(Logical /*new_state*/)
|
|
{
|
|
Check(this);
|
|
// defaults to no-operation: intended to be processed by
|
|
// platform-specific derived class
|
|
Check_Fpu();
|
|
}
|
|
|
|
//#############################################################################
|
|
// Construction and Destruction
|
|
//
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
VTVControlsMapper::VTVControlsMapper(
|
|
VTV *owner,
|
|
int subsystem_ID,
|
|
SubsystemResource *subsystem_resource,
|
|
SharedData &shared_data
|
|
):
|
|
VTVSubsystem(
|
|
owner,
|
|
subsystem_ID,
|
|
subsystem_resource,
|
|
shared_data
|
|
),
|
|
averageOfForwardThrustDemand(4) // ECH 8/3/95 - HACK - To deal with reverse thrust
|
|
{
|
|
if (owner->GetInstance() == VTV::MasterInstance && owner->IsDynamic())
|
|
{
|
|
SetPerformance(&VTVControlsMapper::InterpretControls);
|
|
}
|
|
|
|
stickPosition.x = 0.0f;
|
|
stickPosition.y = 0.0f;
|
|
throttlePosition = 0.0f;
|
|
pedalsPosition = 0.0f;
|
|
|
|
reverseThrust = 0;
|
|
liftCut = 0;
|
|
sideSlip = 0;
|
|
powerDemand = Vector3D::Identity;
|
|
angularVelocityDemand = Vector3D::Identity;
|
|
brakeLights = 0;
|
|
lookLeft = 0;
|
|
lookRight = 0;
|
|
lookBehind = 0;
|
|
lookUp = 0;
|
|
hornBlast = 0;
|
|
controlMode = BasicMode;
|
|
forwardThrustDemand = 0.0f;
|
|
liftCutEngaged = False;
|
|
reverseThrustEngaged = False;
|
|
|
|
pttStatus = 0;
|
|
previousPTTStatus = 0;
|
|
|
|
Check_Fpu();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
VTVControlsMapper::~VTVControlsMapper()
|
|
{
|
|
Check(this);
|
|
Check_Fpu();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
Logical
|
|
VTVControlsMapper::TestInstance() const
|
|
{
|
|
return IsDerivedFrom(*GetClassDerivations());
|
|
}
|