Files
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

704 lines
17 KiB
C++

#include "MW4Headers.hpp"
#include "MFB.hpp"
#include "gameinfo.hpp"
#include "Mech.hpp"
#include "Weapon.hpp"
#include "MWDamageObject.hpp"
#include "CombatAI.hpp"
#include "HeatManager.hpp"
#include <Adept\EntityManager.hpp>
#include <Adept\DamageObject.hpp>
#include <Adept\Effect.hpp>
//#############################################################################
//############################### MFB ###################################
//#############################################################################
const StateEngine::StateEntry
MFB::ExecutionStateEngine::StateEntries[]=
{
STATE_ENTRY(MFB__ExecutionStateEngine, Repair),
STATE_ENTRY(MFB__ExecutionStateEngine, OpeningMotion)
};
MFB::ExecutionStateEngine::ClassData*
MFB::ExecutionStateEngine::DefaultData = NULL;
ChainOf<MFB*>
*MFB::s_MFBs = NULL;
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
MFB::ExecutionStateEngine::InitializeClass()
{
Check_Object(BaseClass::DefaultData);
Verify(!DefaultData);
DefaultData =
new ClassData(
MFB__ExecutionStateEngineClassID,
"MFB::ExecutionStateEngine",
BaseClass::DefaultData,
ELEMENTS(StateEntries), StateEntries,
(Entity::ExecutionStateEngine::Factory)Make,
(Entity::ExecutionStateEngine::FactoryRequest::Factory)
&FactoryRequest::ConstructFactoryRequest
);
Register_Object(DefaultData);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
MFB::ExecutionStateEngine::TerminateClass()
{
Unregister_Object(DefaultData);
delete DefaultData;
DefaultData = NULL;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MFB::ExecutionStateEngine*
MFB::ExecutionStateEngine::Make(
MFB *vehicle,
FactoryRequest *request
)
{
Check_Object(vehicle);
Check_Object(request);
gos_PushCurrentHeap(Heap);
MFB::ExecutionStateEngine *engine =
new MFB::ExecutionStateEngine(DefaultData, vehicle, request);
gos_PopCurrentHeap();
return engine;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
MFB::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 0
MFB *mfb = Cast_Object(MFB*, owningEntity);
Check_Object(mfb);
#endif
switch (BaseClass::RequestState(new_state, data))
{
case OpeningMotionState:
// mfb->animStateEngine->RequestState(MechAnimationStateEngine::OpeningState);
break;
}
return currentState;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
MFB::ExecutionStateEngine::TestInstance() const
{
Verify(IsDerivedFrom(DefaultData));
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MFB::ClassData*
MFB::DefaultData = NULL;
DWORD MechWarrior4::Executed_MFB_Count = 0;
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
MFB::InitializeClass()
{
Check_Object(MWMover::ExecutionStateEngine::DefaultData);
Verify(!DefaultData);
DefaultData =
new ClassData(
MFBClassID,
"MechWarrior4::MFB",
BaseClass::DefaultData,
0, NULL,
(Entity::Factory)Make,
(Entity::CreateMessage::Factory)CreateMessage::ConstructCreateMessage,
ExecutionStateEngine::DefaultData,
(Entity::GameModel::Factory)GameModel::ConstructGameModel,
(Entity::GameModel::Factory)GameModel::ConstructOBBStream,
(Entity::GameModel::ReadAndVerifier)GameModel::ReadAndVerify,
(Entity::GameModel::ModelWrite)GameModel::WriteToText,
(Entity::GameModel::ModelSave)GameModel::SaveGameModel,
AnimationStateEngine::Make
);
Register_Object(DefaultData);
DIRECT_GAME_MODEL_ATTRIBUTE(
MFB__GameModel,
RepairEffectResource,
m_repairEffectResource,
ResourceID
);
DIRECT_GAME_MODEL_ATTRIBUTE(
MFB__GameModel,
RepairCenterOffset,
m_repairCenterOffset,
Stuff::Vector3D
);
DIRECT_GAME_MODEL_ATTRIBUTE(
MFB__GameModel,
RepairDistance,
m_repairDistance,
Stuff::Scalar
);
s_MFBs = new ChainOf<MFB*>(NULL);
Check_Pointer(s_MFBs);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
MFB::TerminateClass()
{
Unregister_Object(DefaultData);
delete DefaultData;
DefaultData = NULL;
s_MFBs->DeletePlugs();
delete s_MFBs;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MFB*
MFB::Make(
CreateMessage *message,
ReplicatorID *base_id
)
{
Check_Object(message);
gos_PushCurrentHeap(Heap);
MFB *new_entity = new
MFB(DefaultData, message, base_id, NULL);
gos_PopCurrentHeap();
Check_Object(new_entity);
Check_Object(EntityManager::GetInstance());
EntityManager::GetInstance()->AddMover(new_entity);
return new_entity;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MFB::MFB(
ClassData *class_data,
CreateMessage *message,
ReplicatorID *base_id,
ElementRenderer::Element *element
):
Vehicle(class_data, message, base_id, element),
m_repairEffect(NULL),
m_repairingMech(NULL)
{
Check_Pointer(this);
Check_Object(message);
HookUpSubsystems();
CommonCreation(message);
s_MFBs->Add(this);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MFB::~MFB()
{
if(m_weaponIterator)
{
delete m_weaponIterator;
}
if(m_internalIterator)
{
delete m_internalIterator;
}
if(m_armorIterator)
{
delete m_armorIterator;
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
MFB::Reuse(
const CreateMessage *message,
ReplicatorID *base_id
)
{
Check_Object(this);
Check_Object(message);
STOP(("Not implemented"));
BaseClass::Reuse(message, base_id);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
MFB::CommonCreation(CreateMessage *message)
{
Check_Object(this);
Check_Object(message);
//m_RepairDistance = 18.0f;
m_CurrentMechUsing = NULL;
m_currentState = FixingGimpState;
m_nextRepairTime = gos_GetElapsedTime();
m_weaponIterator = NULL;
m_internalIterator = NULL;
m_armorIterator = NULL;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
MFB::Respawn(Entity::CreateMessage *message)
{
Check_Object(this);
Check_Object(message);
BaseClass::Respawn(message);
CreateMessage *vehicle_message = Cast_Pointer(CreateMessage *, message);
CommonCreation(vehicle_message);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void MFB::TurnOn (void)
{
lastParameterization = gos_GetElapsedTime ();
executionState->RequestState(ExecutionStateEngine::DrivingMotionState);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
MFB::PreCollisionExecute(Time till)
{
Check_Object(this);
PRECOLLISION_LOGIC("MFB");
#if defined(LAB_ONLY)
if (!instanceName)
MWGameInfo::g_LastVehicle[0] = '\0';
else
{
strncpy(MWGameInfo::g_LastVehicle, instanceName, sizeof(MWGameInfo::g_LastVehicle)-1);
MWGameInfo::g_LastVehicle[sizeof(MWGameInfo::g_LastVehicle)-1] = '\0';
}
MWGameInfo::g_LastVehiclePos = GetLocalToWorld ();
#endif
//
//-------------------------------------------------------------
// Let the base class deal with setting up the motion variables
//-------------------------------------------------------------
//
BaseClass::PreCollisionExecute(till);
Set_Statistic(Executed_MFB_Count, Executed_MFB_Count+1);
//
//----------------------------------------------------------------------
// Any executing vehicle should not be in never execute state and should
// always use post collision
//----------------------------------------------------------------------
//
Check_Object(executionState);
int pre_state = executionState->GetState();
Verify(pre_state != ExecutionStateEngine::NeverExecuteState);
//
//-------------------------------------------------------------------------
// Do the appropriate type of simulation. Animated motion state is for BRB
//-------------------------------------------------------------------------
//
Scalar time_slice = GetTimeSlice(till);
switch (pre_state)
{
//
//----------------------------------------------------
// Always execute state is for the normal driving code
//----------------------------------------------------
//
case ExecutionStateEngine::DrivingMotionState:
// ComputeForwardSpeed(time_slice);
// UpdateVehiclePosition(time_slice);
break;
//
//-----------------------------------------------------------
//We need to repair now...some mech powered down inside of us
//-----------------------------------------------------------
//
case ExecutionStateEngine::RepairState:
if(gos_GetElapsedTime() > m_nextRepairTime)
RepairTargetMech(time_slice);
break;
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
MFB::PostCollisionExecute(Time till)
{
Check_Object(this);
Check_Object(executionState);
POSTCOLLISION_LOGIC("MFB");
switch(executionState->GetState())
{
case ExecutionStateEngine::RepairState:
CreateRepairEffect();
break;
}
BaseClass::PostCollisionExecute(till);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
MFB::CollisionHandler(
Stuff::LinearMatrix4D *new_position,
Stuff::DynamicArrayOf<CollisionData> *collisions
)
{
Check_Object(this);
Verify(GetInterestLevel() != DormantInterestLevel);
Verify(EntityManager::GetInstance()->IsInPostCollisionExecution(this));
Verify(!newCollisions);
delete collisions;
return false;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
MFB::RepairTargetMech(Scalar time_slice)
{
Check_Object(this);
Mech *mech = m_repairingMech.GetCurrent();
if(mech)
{
if (mech->vehicleInterface)
{
mech->vehicleInterface->ReactToEvent (VehicleInterface::MISSILE_LOCKLOST);
}
Check_Object(mech);
if(!mech->vehicleShutDown)
{
ShutMFBDown();
return;
}
switch(m_currentState)
{
case FixingGimpState:
{
//First check to see if we fix gimp
if(mech->IsGimped())
{
mech->RepairGimp();
m_nextRepairTime = gos_GetElapsedTime() + 6.0f;
m_currentState = ReloadingAmmoState;
return;
}
}
case ReloadingAmmoState:
{
Weapon *weapon;
m_currentState = ReloadingAmmoState;
while((weapon = m_weaponIterator->ReadAndNext()) != NULL)
{
Check_Object(weapon);
if(weapon->HasFiredAmmo())
{
weapon->Reload();
m_nextRepairTime = gos_GetElapsedTime() + 2.0f;
return;
}
}
}
case ReloadingCoolantState:
{
m_currentState = ReloadingCoolantState;
Check_Object(mech->m_heatManager);
if(mech->m_heatManager->GetCoolant() < mech->m_heatManager->GetMaxCoolant())
{
mech->m_heatManager->AddCoolant(mech->m_heatManager->GetMaxCoolant());
m_nextRepairTime = gos_GetElapsedTime() + 2.0f;
return;
}
}
case FixingInternalState:
{
MWInternalDamageObject *internal;
m_currentState = FixingInternalState;
while((internal = m_internalIterator->ReadAndNext()) != NULL)
{
Check_Object(internal);
if(internal->CanRepair())
{
internal->Reuse();
m_nextRepairTime = gos_GetElapsedTime() + 0.10f;
return;
}
}
}
case FixingArmorState:
{
DamageObject *armor;
m_currentState = FixingArmorState;
while((armor = m_armorIterator->ReadAndNext()) != NULL)
{
Check_Object(armor);
if(armor->CanRepair())
{
armor->Reuse();
// MSL 5.05 Changed Repair time for external armor
// m_nextRepairTime = gos_GetElapsedTime() + 0.10f;
m_nextRepairTime = gos_GetElapsedTime() + 1.0f;
return;
}
}
}
}
mech->ShutDownRequest(false);
if (mech->vehicleInterface)
{
mech->vehicleInterface->ReactToEvent (VehicleInterface::REPAIR_STOP);
}
ShutMFBDown();
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
MFB::StartNewMech(Mech *new_mech)
{
Check_Object(this);
Check_Object(new_mech);
m_repairingMech.Remove();
m_repairingMech.Add(new_mech);
m_currentState = FixingGimpState;
if(m_weaponIterator)
{
delete m_weaponIterator;
}
m_weaponIterator = new WeaponIteratorType(&new_mech->weaponChain);
if(m_internalIterator)
{
delete m_internalIterator;
}
m_internalIterator = new InternalIteratorType(&new_mech->internalDamageObjects);
if(m_armorIterator)
{
delete m_armorIterator;
}
m_armorIterator = new ArmorIteratorType(&new_mech->damageObjects);
Check_Object(executionState);
executionState->RequestState(ExecutionStateEngine::RepairState);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
MFB::ShutMFBDown()
{
Check_Object(this);
Check_Object(executionState);
executionState->RequestState(ExecutionStateEngine::NeverExecuteState);
if(m_repairEffect.GetCurrent())
{
m_repairEffect.GetCurrent()->executionState->RequestState(Effect::ExecutionStateEngine::StoppingState);
m_repairEffect.Remove();
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
MFB::CreateRepairEffect()
{
Check_Object(this);
if(!m_repairEffect.GetCurrent())
{
const GameModel *model = GetGameModel();
Check_Object(model);
Effect *effect =
CreateLoopingEffect(model->m_repairEffectResource, this);
if(effect)
m_repairEffect.Add(effect);
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
MFB::IsWithin(
Entity *target,
Vector3D offset,
Scalar distance,
bool ignorey
)
{
Check_Object(this);
Check_Object(target);
Vector3D target_line = Vector3D::Identity;
Point3D our_position;
Point3D target_position;
target_position = target->GetLocalToWorld();
our_position.Multiply(offset, GetLocalToWorld());
Point3D our_translation;
our_translation = GetLocalToWorld();
our_position.Add(our_position, our_translation);
if ((our_position.y < -1500) || (target_position.y < -1500)) // units in hell are never near each other
return false;
if (ignorey)
{
our_position.y = 0;
target_position.y = 0;
}
target_line.Subtract(target_position, our_position);
return target_line.GetLengthSquared() <= (distance * distance);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MFB*
MFB::IsWithinMFB(Mech *mech)
{
Check_Object(mech);
MFB *mfb;
ChainIteratorOf<MFB *> iterator(s_MFBs);
while((mfb = iterator.ReadAndNext()) != NULL)
{
Check_Object(mfb);
//make sure the mfb is not destroyed and the mech is within 10.0 meters
if(!mfb->IsDestroyed())
{
if(mfb->IsWithin(mech, mfb->GetGameModel()->m_repairCenterOffset, mfb->GetGameModel()->m_repairDistance))
{//if(mech->IsWithin(mfb, mfb->GetGameModel()->m_repairDistance))
// MSL 5.03 Checks for Team Alignment
// If your not the same alignment as the MFB you can't use it
// MSL 5.05 Checks and allows for No Team
if ((mfb->GetAlignment() == mech->GetAlignment()) ||
(mech->GetTeam() == MWApplication::No_Team))
{
if (mech->vehicleInterface)
{
mech->vehicleInterface->ReactToEvent (VehicleInterface::REPAIR_START);
}
mfb->StartNewMech(mech);
return mfb;
}
}
}
}
return NULL;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
MFB::ReactToDestruction(int damage_mode, int damage_type)
{
Check_Object(this);
if(!IsDestroyed())
{
if(m_AI)
{
Check_Object(m_AI);
m_AI->Die();
}
SetDestroyedFlag(damage_mode);
if (damage_type != SplashDamageType)
DealSplashDamage();
MW4AI::g_Rect4DHash->RemovePermRect (this);
const GameModel *model = GetGameModel();
Check_Object(model);
CreateEffect(model->destroyedEffectResource, (Point3D)GetLocalToWorld());
Entity *death_entity = CreateStaticHermitEntity(model->deathEntityResource);
if((death_entity) && (!m_deathEntity.GetCurrent()))
{
m_deathEntity.Remove();
m_deathEntity.Add(death_entity);
}
RemoveFromExecution();
}
Entity::ReactToDestruction(damage_mode, damage_type);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
MFB::TestInstance() const
{
Verify(IsDerivedFrom(DefaultData));
}