Files
RP412/RP/VTVMPPR.cpp
T
CydandClaude Fable 5 af52476603 The pod can drive a scripted lap
RP412INPUTSCRIPT names a timeline file - one row per change, throttle,
stick X and Y, pedals, held until the next row's time - and the pod
drives it instead of listening to the controls. Times are SIMULATION
seconds from the green light, evaluated per step in the one place every
mapper funnels through (VTVControlsMapper::InterpretControls), so the
same script is the same lap at any frame rate. Rows hold rather than
interpolate on purpose: interpolation would sample differently at
different physics rates, and nothing on this path is allowed to.

The script shares the green-light anchor with RP412PHYSTRACE - its
clock starts at the instant the vehicle is stopped dead - because a
timeline that starts when the loader happens to finish is a different
lap every run.

A race is only deterministic if somebody DRIVES it, and a human cannot
drive the same lap twice. The first scripted lap - full throttle, a
steer, a crash at speed - earned the harness immediately:

- The drive, the crash, the death and the respawn teleport were all
  BIT-EXACT between identical runs, through t=13.5. Collisions with
  world geometry and the damage path are step-deterministic, which is
  better news than the code reading suggested.

- The first divergence is the step AFTER the respawn: the DropZoneReply
  that stands a dead pod back up is posted at wall-clock Now()+1.0
  (RPPLAYER.cpp), so the reset lands on a different sim step every run
  and everything after is time-shifted. The crash is deterministic; the
  RECOVERY is not. That is the next fix, and it is now a measurement,
  not a theory.

Values are clamped at load, once and visibly, so a script asking for
throttle 2.0 cannot trip the mapper's own range Verifies. Off unless
the environment names a file; it would be a cheat in a real race.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-09 21:03:03 -05:00

990 lines
26 KiB
C++

#include "rp.h"
#pragma hdrstop
#include "vtvmppr.h"
#include "vtvpwr.h"
#include "..\munga\icom.h"
#include "..\munga\app.h"
#include "..\munga\inputscript.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));
//
//----------------------------------------------------------------
// RP412INPUTSCRIPT: scripted driving, on this subsystem's own step
// clock.
//
// This is the one place every mapper - RIO, Thrustmaster, pad -
// funnels through, and it runs per SIMULATION STEP, so a scripted
// value lands on the same step of every run whatever the frame
// rate. Overriding at the RIO or the controls manager would key
// the timeline to the frame loop, which is wall clock, which is
// the thing the whole harness exists to keep out of the physics.
//
// Only the player's own vehicle: replicants get their state from
// the network, and the mapper does not run for them anyway.
//----------------------------------------------------------------
//
if (RPInputScript_Active())
{
float script_throttle, script_x, script_y, script_pedals;
if (RPInputScript_Sample(GetLastPerformance(),
&script_throttle, &script_x, &script_y, &script_pedals))
{
throttlePosition = script_throttle;
stickPosition.x = script_x;
stickPosition.y = script_y;
pedalsPosition = script_pedals;
}
}
//
//----------------------------------------------
// 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());
}