Player request from the six-player night: a pod that dies at full throttle reappears under a hand still holding full throttle, and launches on it. The pod should respawn with the throttle at zero and stay there until the player has brought the control back to zero themselves. The arcade never met the problem in this form - its throttle was a physical lever that kept its position regardless - but the same idea protects a real lever too, which matters now that the pods want this code. The latch lives in VTVControlsMapper, the one place every control source - RIO lever, pad trigger, keyboard axis, input script - funnels through, per simulation step, so it is deterministic and local to the owning machine by construction (the mapper only runs on MasterInstance). VTV::Reset arms it through DeathReset, with one wrinkle: the controls mapper is subsystem ZERO, below BasicSubsystemCount, so Reset's subsystem loop has never reached it. It is called explicitly now. Only RegularReset latches - that is the death respawn and the first spawn (so a throttle held through the countdown no longer buys a flying start). Football's repositioning reset does not latch, and mission review has no controls. While latched the mapper computes as though the throttle were zero, but only for the pass: the true lever position is restored at the bottom of the function, so the cockpit gauge and the watchers keep showing the player the hand they need to bring down. The restore also keeps the release test honest - RIO analog events arrive on CHANGE, so a zeroed attribute would otherwise sit at zero, release the latch by itself, and hand back a live throttle the moment the hand twitched. Release is at 5% of travel: wide enough for a resting trigger or a real lever's potentiometer sitting a few counts off its stop, narrow enough that easing off does not satisfy it. Proven with the input-script harness, both directions. Throttle held at 1.0 from the green light: the pod moves 1.3cm in 30 seconds, all of it hover settle. Throttle at zero for two seconds then 1.0: the latch releases in the quiet window and the pod is 665m down the track twenty seconds later. The second run is also the normal-play case - a control already at zero releases the latch on the first step, invisibly. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
272 lines
6.2 KiB
C++
272 lines
6.2 KiB
C++
#pragma once
|
|
|
|
#include "vtvsub.h"
|
|
#include "..\munga\average.h"
|
|
|
|
//##########################################################################
|
|
//################ VTVControlsMapper::ModelResource ##################
|
|
//##########################################################################
|
|
|
|
class VTVControlsMapper:
|
|
public VTVSubsystem
|
|
{
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// Shared Data support
|
|
//
|
|
public:
|
|
static Derivation *GetClassDerivations();
|
|
static SharedData DefaultData;
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// Messaging Support
|
|
//
|
|
public:
|
|
enum {
|
|
ConfigureControlsMessageID = VTVSubsystem::NextMessageID,
|
|
ConfigureSideSlipMappablesMessageID,
|
|
ChooseSideSlipMessageID,
|
|
ConfigureLiftCutMappablesMessageID,
|
|
ChooseLiftCutMessageID,
|
|
ConfigureHornMappablesMessageID,
|
|
ChooseHornMessageID,
|
|
ActivateHornMessageID,
|
|
ConfigurePTTMappablesMessageID,
|
|
ChoosePTTMessageID,
|
|
ActivatePTTMessageID,
|
|
ToggleReticleMessageID,
|
|
NextMessageID
|
|
};
|
|
|
|
void
|
|
ConfigureControlsMessageHandler(
|
|
ReceiverDataMessageOf<ControlsButton> *message
|
|
);
|
|
void
|
|
ConfigureSideSlipMappablesMessageHandler(
|
|
ReceiverDataMessageOf<ControlsButton> *message
|
|
);
|
|
void
|
|
ChooseSideSlipMessageHandler(
|
|
ReceiverDataMessageOf<ControlsButton> *message
|
|
);
|
|
void
|
|
ConfigureLiftCutMappablesMessageHandler(
|
|
ReceiverDataMessageOf<ControlsButton> *message
|
|
);
|
|
void
|
|
ChooseLiftCutMessageHandler(
|
|
ReceiverDataMessageOf<ControlsButton> *message
|
|
);
|
|
void
|
|
ConfigureHornMappablesMessageHandler(
|
|
ReceiverDataMessageOf<ControlsButton> *message
|
|
);
|
|
void
|
|
ChooseHornMessageHandler(
|
|
ReceiverDataMessageOf<ControlsButton> *message
|
|
);
|
|
void
|
|
ActivateHornMessageHandler(
|
|
ReceiverDataMessageOf<ControlsButton> *message
|
|
);
|
|
void
|
|
ConfigurePTTMappablesMessageHandler(
|
|
ReceiverDataMessageOf<ControlsButton> *message
|
|
);
|
|
void
|
|
ChoosePTTMessageHandler(
|
|
ReceiverDataMessageOf<ControlsButton> *message
|
|
);
|
|
void
|
|
ActivatePTTMessageHandler(
|
|
ReceiverDataMessageOf<ControlsButton> *message
|
|
);
|
|
void
|
|
ToggleReticleMessageHandler(
|
|
ReceiverDataMessageOf<ControlsButton> *message
|
|
);
|
|
|
|
private:
|
|
static const HandlerEntry MessageHandlerEntries[];
|
|
|
|
protected:
|
|
//static MessageHandlerSet MessageHandlers;
|
|
static MessageHandlerSet& GetMessageHandlers();
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// Attribute Support
|
|
//
|
|
public:
|
|
enum {
|
|
StickPositionAttributeID = VTVSubsystem::NextAttributeID,
|
|
ThrottlePositionAttributeID,
|
|
PedalsPositionAttributeID,
|
|
ReverseThrustAttributeID,
|
|
LiftCutAttributeID,
|
|
SideSlipAttributeID,
|
|
PowerDemandAttributeID,
|
|
AngularVelocityDemandAttributeID,
|
|
BrakeLightsAttributeID,
|
|
LookLeftAttributeID,
|
|
LookRightAttributeID,
|
|
LookBehindAttributeID,
|
|
LookUpAttributeID,
|
|
ControlModeAttributeID,
|
|
HornBlastAttributeID,
|
|
MustMatchAttributeID,
|
|
ReverseThrustEngagedAttributeID, // ECH 8/3/95 - HACK - deal with thrust
|
|
LiftCutEngagedAttributeID,
|
|
PTTStatusAttributeID,
|
|
NextAttributeID
|
|
};
|
|
|
|
private:
|
|
static const IndexEntry AttributePointers[];
|
|
|
|
protected:
|
|
//static AttributeIndexSet AttributeIndex
|
|
static AttributeIndexSet& GetAttributeIndex();
|
|
|
|
//
|
|
// public attribute declarations go here
|
|
//
|
|
public:
|
|
ControlsJoystick stickPosition;
|
|
Scalar throttlePosition;
|
|
Scalar pedalsPosition;
|
|
ControlsButton
|
|
reverseThrust,
|
|
liftCut,
|
|
sideSlip,
|
|
rollLeft,
|
|
rollRight,
|
|
pitchUp,
|
|
pitchDown;
|
|
Vector3D
|
|
powerDemand,
|
|
angularVelocityDemand;
|
|
|
|
int brakeLights;
|
|
ControlsButton
|
|
lookLeft,
|
|
lookRight,
|
|
lookBehind,
|
|
lookUp,
|
|
hornBlast,
|
|
pttStatus;
|
|
|
|
// ECH 8/3/95 - HACK - deal with thrust
|
|
AverageOf<Scalar>
|
|
averageOfForwardThrustDemand;
|
|
Scalar
|
|
forwardThrustDemand;
|
|
Logical
|
|
reverseThrustEngaged;
|
|
|
|
Logical
|
|
liftCutEngaged;
|
|
|
|
enum {
|
|
BasicMode = 0,
|
|
StandardMode,
|
|
VeteranMode,
|
|
MasterMode
|
|
}
|
|
controlMode;
|
|
|
|
unsigned int
|
|
mustMatch,
|
|
mayMatch;
|
|
|
|
protected:
|
|
ControlsButton
|
|
previousPTTStatus; // used to detect change in PTT status
|
|
|
|
//
|
|
// True from a respawn until the throttle control has been seen at
|
|
// (near) zero. While set, InterpretControls behaves as though the
|
|
// throttle were zero, whatever the hand is doing; the attribute
|
|
// itself keeps the real lever value so the cockpit gauge shows the
|
|
// player what they must bring down. See DeathReset.
|
|
//
|
|
Logical
|
|
throttleLatched;
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// Model Support
|
|
//
|
|
public:
|
|
enum {
|
|
ConfigurationState = VTVSubsystem::StateCount,
|
|
StateCount
|
|
};
|
|
|
|
typedef void
|
|
(VTVControlsMapper::*Performance)(Scalar time_slice);
|
|
|
|
void
|
|
SetPerformance(Performance performance)
|
|
{
|
|
Check(this);
|
|
activePerformance = (Simulation::Performance)performance;
|
|
}
|
|
|
|
void
|
|
InterpretControls(Scalar time_slice);
|
|
void
|
|
SetConfigurationState(Logical enter_config);
|
|
|
|
//
|
|
// Latch the throttle at zero until the player's control comes back to
|
|
// zero. Called by VTV::Reset on a regular reset - a death respawn, and
|
|
// the first spawn - because the hand that was holding full throttle
|
|
// when the pod died is still holding it when the pod reappears, and
|
|
// the pod used to launch on it. The arcade never had the problem the
|
|
// same way: its throttle was a physical lever the pilot had to move
|
|
// regardless.
|
|
//
|
|
// Football and mission review resets do not latch: one repositions
|
|
// every pod mid-game where the friction is unwanted, the other has no
|
|
// controls at all.
|
|
//
|
|
void
|
|
DeathReset(int reset_command);
|
|
|
|
virtual void
|
|
CreateTemporaryEventMappings(
|
|
Receiver *receiver,
|
|
Receiver::MessageID config_message_id
|
|
),
|
|
RemoveTemporaryEventMappings(),
|
|
AddOrErase(
|
|
unsigned int button_ID,
|
|
Receiver *target,
|
|
Receiver::MessageID message_ID
|
|
),
|
|
AddOrErase(
|
|
unsigned int button_ID,
|
|
ControlsButton *destination
|
|
);
|
|
|
|
virtual void
|
|
NotifyOfControlModeChange(int new_mode);
|
|
|
|
virtual void
|
|
NotifyOfConfigurationModeChange(Logical new_state);
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// Construction and Destruction
|
|
//
|
|
public:
|
|
VTVControlsMapper(
|
|
VTV *owner,
|
|
int subsystem_ID,
|
|
SubsystemResource *subsystem_resource,
|
|
SharedData &shared_data
|
|
);
|
|
~VTVControlsMapper();
|
|
|
|
Logical
|
|
TestInstance() const;
|
|
};
|