Files
firestorm/Gameleap/code/mw4/Code/MW4/JumpJet.cpp
T
Cyd 2b8ca921cb Initial full mirror of c:\VWE (source + assets + toolchain + outputs) via Git LFS
Complete disaster-recovery snapshot: engine/game source, game data assets,
VC6 toolchain + DX SDKs, build outputs, deployed game, and _UNUSED archive.
Large binaries in Git LFS; text preserved byte-for-byte (core.autocrlf=false,
no eol attributes). See RECOVERY.md for the one-clone rebuild procedure.
2026-06-24 21:28:16 -05:00

718 lines
19 KiB
C++

//===========================================================================//
// File: JumpJet.cpp
//---------------------------------------------------------------------------//
// Date Who Modification //
// -------- --- ---------------------------------------------------------- //
// 05/18/99 DPB Created File
//---------------------------------------------------------------------------//
// Copyright (C) 1998, Fasa Interactive //
// All Rights reserved worldwide //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL //
//===========================================================================//
#include "MW4Headers.hpp"
#include "JumpJet.hpp"
#include "Mech.hpp"
#include "VehicleInterface.hpp"
#include "SubsystemClassData.hpp"
#include <Adept\Effect.hpp>
#include <Adept\Map.hpp>
#include <MLR\MLRTexturePool.hpp>
#include <Adept\Site.hpp>
#include "ai_userconstants.hpp"
//#############################################################################
//########################## ExecutionStateEngine #######################
//#############################################################################
JumpJet::ExecutionStateEngine::ClassData*
JumpJet::ExecutionStateEngine::DefaultData = NULL;
const StateEngine::StateEntry
JumpJet::ExecutionStateEngine::StateEntries[]=
{
STATE_ENTRY(JumpJet__ExecutionStateEngine, Jumping),
STATE_ENTRY(JumpJet__ExecutionStateEngine, Recharging)
};
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
JumpJet::ExecutionStateEngine::InitializeClass()
{
Check_Object(StateEngine::DefaultData);
Verify(!DefaultData);
DefaultData =
new ClassData(
JumpJet__ExecutionStateEngineClassID,
"JumpJet::ExecutionStateEngine",
BaseClass::DefaultData,
ELEMENTS(StateEntries), StateEntries,
(Entity::ExecutionStateEngine::Factory)Make,
(Entity::ExecutionStateEngine::FactoryRequest::Factory)
&FactoryRequest::ConstructFactoryRequest
);
Register_Object(DefaultData);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
JumpJet::ExecutionStateEngine::TerminateClass()
{
Unregister_Object(DefaultData);
delete DefaultData;
DefaultData = NULL;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
JumpJet::ExecutionStateEngine*
JumpJet::ExecutionStateEngine::Make(
JumpJet *jump_jet,
FactoryRequest *request
)
{
Check_Object(jump_jet);
Check_Object(request);
gos_PushCurrentHeap(Heap);
JumpJet::ExecutionStateEngine *engine =
new JumpJet::ExecutionStateEngine(DefaultData, jump_jet, request);
gos_PopCurrentHeap();
return engine;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void MechWarrior4::JumpJetSecurityCheckStart()
{
_asm
{
nop
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
JumpJet::ExecutionStateEngine::RequestState(
int new_state,
void* data
)
{
Check_Object(this);
Check_Object(owningEntity);
//
//-------------------------------------------
// If there is no change of state just return
//-------------------------------------------
//
if (new_state == currentState)
return currentState;
//
//---------------------------------------------------
// If we have less than one second charge, don't jump
//---------------------------------------------------
//
JumpJet *jump_jet;
jump_jet = Cast_Object(JumpJet*, owningEntity);
if (new_state == JumpingState && DECRYPT(jump_jet->m_currentEncryptedCharge) < 0.25f)
return currentState;
if(new_state == DestroyedState)
return currentState;
//
//--------------------------------------------------------------------
// Look for the transition type. Our base class should handle destroy
// and never execute commands, but make sure that we only go into
// zero-g state from jumping state
//--------------------------------------------------------------------
//
switch (BaseClass::RequestState(new_state, data))
{
case JumpingState:
{
jump_jet->UsePostCollision();
jump_jet->lastExecuted = gos_GetElapsedTime();
jump_jet->m_needToCreateEffects = true;
//
//----------------------------------------------------------------
// If the mech is walking right now, we launch the initial effects
//----------------------------------------------------------------
//
Check_Object(jump_jet->m_parentMech);
Mech::ExecutionStateEngine *engine =
Cast_Object(Mech::ExecutionStateEngine *, jump_jet->m_parentMech->executionState);
Check_Object(engine);
if (engine->GetState() == engine->DrivingMotionState)
jump_jet->m_needToCreateInitialEffects = true;
jump_jet->lastParameterization = gos_GetElapsedTime();
}
break;
case NeverExecuteState:
if (oldState != RechargingState)
jump_jet->CleanUpEffects();
jump_jet->m_needToCreateEffects = false;
jump_jet->m_needToCreateInitialEffects = false;
jump_jet->lastParameterization = gos_GetElapsedTime();
break;
case RechargingState:
if (oldState != NeverExecuteState)
jump_jet->CleanUpEffects();
jump_jet->m_needToCreateEffects = false;
jump_jet->m_needToCreateInitialEffects = false;
jump_jet->lastParameterization = gos_GetElapsedTime();
break;
//
//------------------------------------------------------------------
// Our base class has handled destruction, so don't do anything more
//------------------------------------------------------------------
//
case DestroyedState:
break;
default:
STOP(("Shouldn't be here"));
break;
};
return currentState;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
JumpJet::ExecutionStateEngine::TestInstance() const
{
Verify(IsDerivedFrom(DefaultData));
}
//#############################################################################
//############################### JumpJet ###############################
//#############################################################################
JumpJet::ClassData*
JumpJet::DefaultData = NULL;
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
JumpJet::InitializeClass()
{
Verify(!DefaultData);
DefaultData =
new ClassData(
JumpJetClassID,
"MechWarrior4::JumpJet",
BaseClass::DefaultData,
0, NULL,
(Entity::Factory)Make,
(Entity::CreateMessage::Factory)CreateMessage::ConstructCreateMessage,
ExecutionStateEngine::DefaultData,
(Entity::GameModel::Factory)GameModel::ConstructGameModel,
NULL,
(Entity::GameModel::ReadAndVerifier)GameModel::ReadAndVerify,
(Entity::GameModel::ModelWrite)GameModel::WriteToText,
(Entity::GameModel::ModelSave)GameModel::SaveGameModel,
(Subsystem::StreamCreate) CreateStream
);
Register_Object(DefaultData);
DIRECT_GAME_MODEL_ATTRIBUTE(
JumpJet__GameModel,
RechargeTime,
m_rechargeTime,
Scalar
);
DIRECT_GAME_MODEL_ATTRIBUTE(
JumpJet__GameModel,
BurnTime,
m_burnTime,
Scalar,
);
DIRECT_GAME_MODEL_ATTRIBUTE(
JumpJet__GameModel,
FlightTime,
m_flightTime,
Scalar,
);
DIRECT_GAME_MODEL_ATTRIBUTE(
JumpJet__GameModel,
SlipDistance,
m_slipDistance,
Scalar,
);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
//moved it so it won't be in the same spot in the new executable
void JumpJet::PreCollisionExecute(Time till)
{
Check_Object(this);
Check_Object(m_parentMech);
PRECOLLISION_LOGIC("Subsystem::JumpJet");
Stuff::Scalar current_charge = DECRYPT(m_currentEncryptedCharge);
//
//---------------------
// Figure out our state
//---------------------
//
Scalar time_slice = GetTimeSlice(till);
const GameModel* model = GetGameModel();
Check_Object(model);
Check_Object(executionState);
int pre_state = executionState->GetState();
Verify(pre_state != ExecutionStateEngine::NeverExecuteState);
//
//---------------------------------------------------------------------
// If we are jumping, deal with applying the force if we are not out of
// power
//---------------------------------------------------------------------
//
switch (pre_state)
{
case ExecutionStateEngine::JumpingState:
// only the master is allowed to do decrement the charge
// if (!m_EffectOnly)
// {
// everyone is allowed to decrement the charge...
current_charge -= time_slice;
SetCurrentCharge(current_charge);
if (current_charge <= 0.0f)
{
current_charge = 0.0f;
SetCurrentCharge(current_charge);
executionState->RequestState(ExecutionStateEngine::RechargingState);
}
else
{
Vector3D &accel = m_parentMech->localSpaceAcceleration.linearMotion;
Vector3D &velocity = m_parentMech->localSpaceVelocity.linearMotion;
if (velocity.z > 0.0f && m_parentMech->speedDemand <= 0.0f)
accel.z += m_parentMech->speedDemand*m_slide;
else
accel.y += m_acceleration;
if (MW4AI::UserConstants::m_Instance->Get(MW4AI::UserConstants::flying_turn_rate) == 0.0f)
accel.x += m_parentMech->yawDemand * m_slide;
}
// }
break;
//
//-----------------
// Start recharging
//-----------------
//
case ExecutionStateEngine::RechargingState:
// but only the master/server is allowed to increment the jump charge
// if (GetReplicatorMode() == ServerMasterMode || GetReplicatorMode() == MasterMode)
// {
Verify(!m_leftJumpJetEffect.GetCurrent());
Verify(!m_rightJumpJetEffect.GetCurrent());
current_charge += time_slice * m_rechargeRate;
Max_Clamp(current_charge, model->m_burnTime);
SetCurrentCharge(current_charge);
// }
if(current_charge == model->m_burnTime)
executionState->RequestState(ExecutionStateEngine::NeverExecuteState);
break;
}
SPEWALWAYS(("jerryeds", "JUMP FUEL: %f", current_charge));
//
//------------------------------------
// Let our parent deal with things now
//------------------------------------
//
BaseClass::PreCollisionExecute(till);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
JumpJet::TerminateClass()
{
Unregister_Object(DefaultData);
delete DefaultData;
DefaultData = NULL;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
JumpJet*
JumpJet::Make(
CreateMessage *message,
ReplicatorID *base_id
)
{
Check_Object(message);
gos_PushCurrentHeap(Heap);
JumpJet *new_entity =
new JumpJet(DefaultData, message, base_id, NULL);
gos_PopCurrentHeap();
Check_Object(new_entity);
return new_entity;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
JumpJet::JumpJet(
ClassData *class_data,
CreateMessage *message,
ReplicatorID *base_id,
ElementRenderer::Element *element
):
Subsystem(class_data, message, base_id, element),
m_rightJumpJetEffect(NULL),
m_leftJumpJetEffect(NULL),
m_initialRightJumpJetEffect(NULL),
m_initialLeftJumpJetEffect(NULL)
{
Check_Pointer(this);
Check_Object(message);
CommonCreation(message);
m_jumpJetEffectResource = ResourceID::Null;
m_initialJumpJetEffectResource = ResourceID::Null;
m_leftJumpJetSite = NULL;
m_rightJumpJetSite = NULL;
m_parentMech = NULL;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
JumpJet::Respawn(Entity::CreateMessage *message)
{
Check_Object(this);
Check_Object(message);
BaseClass::Respawn(message);
CreateMessage *jump_jet_message = Cast_Pointer(CreateMessage *, message);
CommonCreation(jump_jet_message);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
JumpJet::CommonCreation(CreateMessage *message)
{
Check_Object(this);
Check_Object(message);
const GameModel *model = GetGameModel();
Check_Object(model);
m_needToCreateEffects = false;
m_needToCreateInitialEffects = false;
m_currentEncryptedCharge = ENCRYPT(model->m_burnTime);
Verify(model->m_burnTime > 0);
Verify(model->m_rechargeTime > 0);
m_rechargeRate = model->m_burnTime / model->m_rechargeTime;
//
//------------------------------------------------------------
// Now calculate the acceleration needed to give these numbers
//------------------------------------------------------------
//
Scalar temp = model->m_flightTime - model->m_burnTime;
Verify(temp > 0.0f);
m_acceleration = g_Gravity*temp*temp / (2.0f*model->m_flightTime - model->m_burnTime);
Verify(m_acceleration > g_Gravity);
//
//---------------------------------
// Figure out our side acceleration
//---------------------------------
//
Verify(model->m_flightTime >= 0.0f);
m_slide = model->m_slipDistance / (model->m_burnTime*(model->m_flightTime - 0.5f*model->m_burnTime));
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
JumpJet::~JumpJet()
{
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Stuff::Scalar
JumpJet::CalcBurnTime(
Stuff::Scalar distance,
Stuff::Scalar heightdelta,
Stuff::Scalar forwardvel
)
{
Stuff::Scalar burn,upvel,downvel;
Stuff::Scalar num,denom,ds;
if (forwardvel == 0.0f)
return FLT_MAX;
ds = distance / forwardvel;
upvel = m_acceleration;
downvel = g_Gravity;
num = ((upvel+downvel) * ((upvel*ds*ds) - (2.0f*heightdelta)));
denom = upvel+downvel;
if (num <= SMALL)
return FLT_MAX;
num = Sqrt(num);
Verify (denom != 0.0f);
burn = ds - (num/denom);
return burn;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void MechWarrior4::JumpJetSecurityCheckStop()
{
_asm
{
nop
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
JumpJet::PostCollisionExecute(Time till)
{
Check_Object(this);
POSTCOLLISION_LOGIC("Subsystem::JumpJet");
if(m_needToCreateInitialEffects)
{
CreateInitialJumpEffects();
m_needToCreateInitialEffects = false;
}
if(m_needToCreateEffects)
{
CreateJumpJetEffects();
m_needToCreateEffects = false;
}
IgnorePostCollision();
BaseClass::PostCollisionExecute(till);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
JumpJet::CreateJumpJetEffects()
{
Check_Object(this);
Check_Object(m_parentMech);
Check_Object(m_leftJumpJetSite);
Check_Object(m_rightJumpJetSite);
Verify(executionState->GetState() == ExecutionStateEngine::JumpingState);
Effect *jump_jet_effect;
if(m_leftJumpJetEffect.GetCurrent() == NULL)
{
jump_jet_effect = m_parentMech->CreateLoopingEffect(
m_jumpJetEffectResource,
m_leftJumpJetSite,
true
);
Check_Object(jump_jet_effect);
jump_jet_effect->BecomeInteresting (true);
jump_jet_effect->executionState->RequestState(Effect::ExecutionStateEngine::RunningState);
m_leftJumpJetEffect.Add(jump_jet_effect);
}
if(m_rightJumpJetEffect.GetCurrent() == NULL)
{
jump_jet_effect = m_parentMech->CreateLoopingEffect(
m_jumpJetEffectResource,
m_rightJumpJetSite,
true
);
Check_Object(jump_jet_effect);
jump_jet_effect->BecomeInteresting (true);
jump_jet_effect->executionState->RequestState(Effect::ExecutionStateEngine::RunningState);
m_rightJumpJetEffect.Add(jump_jet_effect);
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
JumpJet::CreateInitialJumpEffects()
{
Check_Object(this);
Check_Object(m_parentMech);
Check_Object(m_leftJumpJetSite);
Check_Object(m_rightJumpJetSite);
Verify(executionState->GetState() == ExecutionStateEngine::JumpingState);
Effect *jump_jet_effect;
if(m_initialLeftJumpJetEffect.GetCurrent() == NULL)
{
jump_jet_effect = m_parentMech->CreateEffect(
m_initialJumpJetEffectResource,
m_leftJumpJetSite,
true
);
if(jump_jet_effect)
m_initialLeftJumpJetEffect.Add(jump_jet_effect);
}
if(m_initialRightJumpJetEffect.GetCurrent() == NULL)
{
jump_jet_effect = m_parentMech->CreateEffect(
m_initialJumpJetEffectResource,
m_rightJumpJetSite,
true
);
if(jump_jet_effect)
m_initialRightJumpJetEffect.Add(jump_jet_effect);
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
JumpJet::ConnectSubsystem()
{
Check_Object(this);
Verify(GetParentVehicle()->IsDerivedFrom(Mech::DefaultData));
m_parentMech = Cast_Pointer(Mech*, GetParentVehicle());
const Mech::GameModel *mech_model = m_parentMech->GetGameModel();
Check_Object(mech_model);
bool return_value = false;
if(m_parentMech->DoesHaveAvailableTonage(mech_model->m_jumpJetTonnage))
return_value = BaseClass::ConnectSubsystem();
if(return_value)
m_parentMech->AddTonage(mech_model->m_jumpJetTonnage);
return return_value;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
//
bool
JumpJet::DisconnectSubsystem()
{
Check_Object(this);
Verify(GetParentVehicle()->IsDerivedFrom(Mech::DefaultData));
m_parentMech = Cast_Pointer(Mech*, GetParentVehicle());
const Mech::GameModel *mech_model = m_parentMech->GetGameModel();
Check_Object(mech_model);
bool return_value = false;
return_value = BaseClass::DisconnectSubsystem();
if(return_value)
m_parentMech->SubtractTonage(mech_model->m_jumpJetTonnage);
return return_value;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
JumpJet::TakeCriticalHit()
{
Check_Object(this);
//Don't do anything here
return false;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
JumpJet::DestroySubsystem()
{
Check_Object(this);
#if 0
BaseClass::DestroySubsystem();
const Mech::GameModel *mech_model = m_parentMech->GetGameModel();
Check_Object(mech_model);
m_parentMech->SubtractTonage(mech_model->m_jumpJetTonnage);
EnterNeverExecuteState();
IgnorePostCollision();
CleanUpEffects();
#endif
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
JumpJet::CleanUpEffects()
{
Check_Object(this);
if(m_leftJumpJetEffect.GetCurrent())
{
m_leftJumpJetEffect.GetCurrent()->executionState->RequestState(
Effect::ExecutionStateEngine::StoppingState);
m_leftJumpJetEffect.Remove();
}
if(m_rightJumpJetEffect.GetCurrent())
{
m_rightJumpJetEffect.GetCurrent()->executionState->RequestState(
Effect::ExecutionStateEngine::StoppingState);
m_rightJumpJetEffect.Remove();
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
JumpJet::TestInstance() const
{
Verify(IsDerivedFrom(DefaultData));
}