Files
RP411/RP/CHUTE.cpp
T
CydandClaude Opus 4.8 4abbf8879f Initial import of Red Planet v4.10 Win32 source
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>
2026-06-30 07:59:51 -05:00

639 lines
14 KiB
C++

#include "rp.h"
#pragma hdrstop
#include "chute.h"
#include "..\munga\fileutil.h"
#include "vtv.h"
#include "..\munga\app.h"
#include "vtvmppr.h"
//#############################################################################
// Shared Data Support
//
Chute::SharedData
Chute::DefaultData(
Chute::GetClassDerivations(),
Chute::MessageHandlers,
Chute::GetAttributeIndex(),
Chute::StateCount
);
Derivation* Chute::GetClassDerivations()
{ static Derivation classDerivations(VTVSubsystem::GetClassDerivations(), "Chute");
return &classDerivations;
}
//#############################################################################
// Messaging Support
//
const Receiver::HandlerEntry
Chute::MessageHandlerEntries[]=
{
MESSAGE_ENTRY(Chute, Activate),
MESSAGE_ENTRY(Chute, ConfigureMappables),
MESSAGE_ENTRY(Chute, ChooseButton)
};
Receiver::MessageHandlerSet
Chute::MessageHandlers(
ELEMENTS(Chute::MessageHandlerEntries),
Chute::MessageHandlerEntries,
VTVSubsystem::GetMessageHandlers()
);
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
Chute::ConfigureMappablesMessageHandler(
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
ActivateMessageID, // activation message ID
ChooseButtonMessageID // configuration message ID
);
}
else
{
ExitConfiguration();
}
Check_Fpu();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
Chute::ActivateMessageHandler(
ReceiverDataMessageOf<ControlsButton> *message
)
{
Check(this);
Check(message);
VTV* vtv = GetEntity();
Check(vtv);
Check(application);
if (
vtv->GetSimulationState() == VTV::BurningState
|| application->GetApplicationState() != Application::RunningMission
)
{
Check_Fpu();
return;
}
if (message->dataContents > 0)
{
switch (GetSimulationState())
{
case Ready:
if (vtv->BoosterOn() || DeviantChute())
{
Check_Fpu();
return;
}
if (chutesRemaining > 0)
{
SetSimulationState(Deploy);
deployTimeUsed = 0.0f;
chuteOn = 1;
--chutesRemaining;
ForceUpdate();
lastPerformance = Now();
AlwaysExecute();
Check_Fpu();
}
break;
case Dragging:
ReleaseChute();
Check_Fpu();
break;
}
}
Check_Fpu();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
Chute::ChooseButtonMessageHandler(
ReceiverDataMessageOf<ControlsButton> *message
)
{
if (message->dataContents > 0)
{
//
//---------------------------------------------------------------------
// Find the control manager of the vehicle, and have it turn off an
// existing mapping of a given type if it is there, otherwise it should
// create a new one
//---------------------------------------------------------------------
//
VTV* vtv = GetEntity();
Check(vtv);
VTVControlsMapper* controls =
Cast_Object(
VTVControlsMapper*,
vtv->GetSubsystem(VTV::ControlsMapperSubsystem)
);
Check(controls);
controls->AddOrErase(message->dataContents, this, ActivateMessageID);
}
Check_Fpu();
}
//#############################################################################
// Attribute Support
//
const Chute::IndexEntry
Chute::AttributePointers[]=
{
ATTRIBUTE_ENTRY(Chute, TimeToReady, timeToReady),
ATTRIBUTE_ENTRY(Chute, ChutesRemaining, chutesRemaining),
ATTRIBUTE_ENTRY(Chute, ChuteDirection, chuteDirection),
ATTRIBUTE_ENTRY(Chute, ScaleFactor, scaleFactor),
ATTRIBUTE_ENTRY(Chute, ChuteOn, chuteOn),
ATTRIBUTE_ENTRY(Chute, PercentDone, percentDone)
};
Chute::AttributeIndexSet& Chute::GetAttributeIndex()
{
static Chute::AttributeIndexSet attributeIndex(ELEMENTS(Chute::AttributePointers), Chute::AttributePointers, VTVSubsystem::GetAttributeIndex());
return attributeIndex;
}
//#############################################################################
// Model Support
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
Chute::ReadUpdateRecord(Simulation::UpdateRecord *message)
{
Check(this);
Check_Pointer(message);
VTVSubsystem::ReadUpdateRecord(message);
UpdateRecord* record = (UpdateRecord*) message;
chuteDirection = record->chuteDirection;
chuteOn = record->chuteOn;
scaleFactor = record->scaleFactor;
lastPerformance = lastUpdate;
Check_Fpu();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
Chute::WriteUpdateRecord(Simulation::UpdateRecord *record, int update_model)
{
Check(this);
Check_Pointer(record);
VTVSubsystem::WriteUpdateRecord(record, update_model);
UpdateRecord* update = (UpdateRecord*) record;
update->recordLength = sizeof(*update);
update->chuteDirection = chuteDirection;
update->chuteOn = chuteOn;
update->scaleFactor = scaleFactor;
Check_Fpu();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
static Vector3D forward(0.0f,0.0f,-1.0f);
void
Chute::ChuteSimulation(Scalar time_slice)
{
//
//-------------------------
// Apply the Chute
//-------------------------
//
percentDone = percentDone = (reloadTime - timeToReady)/reloadTime;
Check_Fpu();
if(percentDone > 1.0f)
{
percentDone = 1.0f;
}
if(percentDone < 0.0f)
{
percentDone = 0.0f;
}
Verify(percentDone >= 0.0f);
Verify(percentDone <= 1.0f);
int sim_state = GetSimulationState();
VTV *vtv = GetEntity();
Check(vtv);
switch(sim_state)
{
case Loading:
timeToReady -= time_slice;
if (timeToReady <=0.0f)
{
SetSimulationState(Ready);
NeverExecute();
}
break;
case Dragging:
if (DeviantChute() || vtv->BoosterOn())
{
ReleaseChute();
}
else
{
ApplyDrag(time_slice);
}
break;
case Deploy:
if (DeviantChute() || vtv->BoosterOn())
{
ReleaseChute();
}
else
{
deployTimeUsed += time_slice;
if (deployTimeUsed >= deployTime)
{
scaleFactor.x = 1.0f;
scaleFactor.y = 1.0f;
scaleFactor.z = 1.0f;
SetSimulationState(Dragging);
ForceUpdate();
}
else
{
deployTimeUsed += time_slice;
scaleFactor.y = scaleFactor.x = ScaleXY();
scaleFactor.z = ScaleZ();
deployTimeUsed += time_slice;
}
AlwaysExecute();
}
ApplyDrag(time_slice);
break;
case Released:
SetSimulationState(Loading);
break;
}
Check_Fpu();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
Chute::PointChute(Scalar)
{
if (GetSimulationState() != Dragging || GetSimulationState() != Deploy)
{
Check_Fpu();
return;
}
VTV* vtv = GetEntity();
Check(vtv);
if (!Small_Enough(vtv->localVelocity.linearMotion.LengthSquared()))
{
chuteDirection.Subtract(vtv->localVelocity.linearMotion, forward);
}
Check_Fpu();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Scalar
Chute::ScaleZ()
{
Scalar
top_half = 2*(deployTimeUsed) -1 / (deployTimeUsed) + 1;
Scalar
bottom_half = 2*(deployTime) -1 / (deployTime) + 1;
Scalar result = top_half/bottom_half;
Check_Fpu();
return result;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Scalar Chute::ScaleXY()
{
Scalar
top_half = (deployTimeUsed)*(deployTimeUsed)*(deployTimeUsed);
Check_Fpu();
Scalar
bottom_half = (deployTime)*(deployTime)*(deployTime);
Check_Fpu();
Scalar result = top_half/bottom_half;
Check_Fpu();
return result;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
Chute::ReleaseChute()
{
SetSimulationState(Released);
chuteOn = 0;
ForceUpdate();
if (GetEntity()->GetInstance() == VTV::ReplicantInstance)
{
NeverExecute();
}
Check_Fpu();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void Chute::ApplyDrag(Scalar)
{
//
//--------------------------------------------------------------------------
// Get pointers to the other subsystems we will have to deal with inside the
// VTV
//--------------------------------------------------------------------------
//
VTV* vtv = GetEntity();
//
// If slow enough automatically release the chute
//
Scalar vel2 = vtv->localVelocity.linearMotion.LengthSquared();
if(vel2 < minVelocity)
{
ReleaseChute();
Check_Fpu();
return;
}
//
// Calculate the direction to point the chute in
//
chuteDirection.Subtract(vtv->localVelocity.linearMotion, forward);
//
// Calculate the dragForce to Apply to the VTV
//
Vector3D force;
force.Negate(vtv->localVelocity.linearMotion);
force *= dragForce;
vtv->ApplyLocalAcceleration(force, chuteOffset);
Check_Fpu();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
Chute::DeviantChute()
{
Check(this);
VTV *vtv = GetEntity();
Check(vtv);
Check_Fpu();
return vtv->localVelocity.linearMotion.z > zDeviation;
}
//#############################################################################
// Damage Support
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
Chute::DeathReset(int reset_command)
{
Check(this);
if (reset_command == VTV::FootballReset)
{
chutesRemaining = chuteCount;
}
if (reset_command != VTV::MissionReviewReset)
{
SetSimulationState(Ready);
timeToReady = 0.0f;
}
NeverExecute();
Check_Fpu();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
Chute::DeathShutdown(int)
{
chuteOn = False;
chuteDirection = Quaternion::Identity;
NeverExecute();
Check_Fpu();
}
//#############################################################################
// Contructor and Destructor
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Chute::Chute(
VTV *entity,
int subsystem_ID,
SubsystemResource *subsystem_resource
):
VTVSubsystem(
entity,
subsystem_ID,
subsystem_resource,
DefaultData,
NULL, // assumes no direct controls mapping
Chute::ActivateMessageID // message ID used for activation
),
scaleFactor(0.0f,0.0f,0.0f)
{
//-------------------------------------------
// Set values
//-------------------------------------------
chuteOn = 0;
chuteCount = subsystem_resource->chuteCount;
chutesRemaining = chuteCount;
reloadTime = subsystem_resource->reloadTime;
dragForce = subsystem_resource->dragForce;
deployTime = subsystem_resource->deployTime;
deployTimeUsed = 0.0f;
EntitySegment *site_front_segment =
entity->GetSegment(subsystem_resource->segmentIndex);
chuteOffset = site_front_segment->GetSegmentToEntity();
minVelocity =
subsystem_resource->minVelocity * subsystem_resource->minVelocity;
zDeviation = subsystem_resource->zDeviation;
timeToReady = 0.0f;
chuteDirection = Quaternion::Identity;
percentDone = 1.0f;
SetSimulationState(Ready);
SetPerformance(&Chute::ChuteSimulation);
NeverExecute();
Check_Fpu();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Chute::~Chute()
{
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
Chute::CreateStreamedSubsystem(
NotationFile *model_file,
const char *model_name,
const char *subsystem_name,
SubsystemResource *subsystem_resource,
NotationFile *subsystem_file,
const ResourceDirectories *directories
)
{
Check(model_file);
Check_Pointer(subsystem_name);
Check_Pointer(subsystem_resource);
Check(subsystem_file);
if (
!Subsystem::CreateStreamedSubsystem(
model_file,
model_name,
subsystem_name,
subsystem_resource,
subsystem_file,
directories
)
)
{
return False;
}
subsystem_resource->subsystemModelSize = sizeof(*subsystem_resource);
subsystem_resource->classID = RegisteredClass::ChuteClassID;
//
// Figure out who our voltage source is
//
if (
!subsystem_file->GetEntry(
subsystem_name,
"ChuteCount",
&subsystem_resource->chuteCount
)
)
{
std::cerr << subsystem_name << " missing ChuteCount!\n";
return False;
}
if (
!subsystem_file->GetEntry(
subsystem_name,
"ReloadTime",
&subsystem_resource->reloadTime
)
)
{
std::cerr << subsystem_name << " missing ReloadTime!\n";
return False;
}
if (
!subsystem_file->GetEntry(
subsystem_name,
"DragForce",
&subsystem_resource->dragForce
)
)
{
std::cerr << subsystem_name << " missing DragForce!\n";
return False;
}
if (
!subsystem_file->GetEntry(
subsystem_name,
"MinVelocity",
&subsystem_resource->minVelocity
)
)
{
std::cerr << subsystem_name << " missing MinVelocity!\n";
return False;
}
if (
!subsystem_file->GetEntry(
subsystem_name,
"DeployTime",
&subsystem_resource->deployTime
)
)
{
std::cerr << subsystem_name << " missing DeployTime!\n";
return False;
}
Scalar degree;
if (
!subsystem_file->GetEntry(
subsystem_name,
"MaxRotation",
&degree
)
)
{
std::cerr << subsystem_name << " missing MaxRotation!\n";
return False;
}
else
{
Radian rad(degree);
subsystem_resource->zDeviation = cos(rad);
}
if (subsystem_resource->dragForce < 0.0f)
{
std::cerr << subsystem_name << " must have a positive dragForce!\n";
return False;
}
return True;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
Chute::TestInstance() const
{
return IsDerivedFrom(*GetClassDerivations());
}