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.
491 lines
13 KiB
C++
491 lines
13 KiB
C++
//===========================================================================//
|
|
// File: Subsystem.cpp
|
|
//---------------------------------------------------------------------------//
|
|
// Date Who Modification //
|
|
// -------- --- ---------------------------------------------------------- //
|
|
// 09/09/98 JSE Inital base class based off of Shadowrun/Adept
|
|
//---------------------------------------------------------------------------//
|
|
// Copyright (C) 1998, Fasa Interactive //
|
|
// All Rights reserved worldwide //
|
|
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL //
|
|
//===========================================================================//
|
|
|
|
#include "MW4Headers.hpp"
|
|
|
|
#include "Subsystem.hpp"
|
|
#include "MWDamageObject.hpp"
|
|
#include "Vehicle.hpp"
|
|
#include "SubsystemClassData.hpp"
|
|
#include "VehicleInterface.hpp"
|
|
#include <Adept\Renderer.hpp>
|
|
|
|
//#############################################################################
|
|
//########################## ExecutionStateEngine #######################
|
|
//#############################################################################
|
|
|
|
Subsystem::ExecutionStateEngine::ClassData*
|
|
Subsystem::ExecutionStateEngine::DefaultData = NULL;
|
|
|
|
const StateEngine::StateEntry
|
|
Subsystem::ExecutionStateEngine::StateEntries[]=
|
|
{
|
|
STATE_ENTRY(Subsystem__ExecutionStateEngine, Damaged),
|
|
STATE_ENTRY(Subsystem__ExecutionStateEngine, Destroyed)
|
|
};
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Subsystem::ExecutionStateEngine::InitializeClass()
|
|
{
|
|
Check_Object(StateEngine::DefaultData);
|
|
Verify(!DefaultData);
|
|
DefaultData =
|
|
new ClassData(
|
|
Subsystem__ExecutionStateEngineClassID,
|
|
"Subsystem::ExecutionStateEngine",
|
|
Entity::ExecutionStateEngine::DefaultData,
|
|
ELEMENTS(StateEntries), StateEntries,
|
|
(Entity::ExecutionStateEngine::Factory)Make,
|
|
(Entity::ExecutionStateEngine::FactoryRequest::Factory)
|
|
&FactoryRequest::ConstructFactoryRequest
|
|
);
|
|
Register_Object(DefaultData);
|
|
}
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Subsystem::ExecutionStateEngine::TerminateClass()
|
|
{
|
|
Unregister_Object(DefaultData);
|
|
delete DefaultData;
|
|
DefaultData = NULL;
|
|
}
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
Subsystem::ExecutionStateEngine*
|
|
Subsystem::ExecutionStateEngine::Make(
|
|
Subsystem *subsystem,
|
|
FactoryRequest *request
|
|
)
|
|
{
|
|
Check_Object(subsystem);
|
|
Check_Object(request);
|
|
gos_PushCurrentHeap(Heap);
|
|
Subsystem::ExecutionStateEngine *engine =
|
|
new Subsystem::ExecutionStateEngine(DefaultData, subsystem, request);
|
|
gos_PopCurrentHeap();
|
|
return engine;
|
|
|
|
}
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
int
|
|
Subsystem::ExecutionStateEngine::RequestState(
|
|
int new_state,
|
|
void* data
|
|
)
|
|
{
|
|
Check_Object(this);
|
|
Check_Object(owningEntity);
|
|
|
|
//
|
|
//------------------------------
|
|
// Quick exit if no state change
|
|
//------------------------------
|
|
//
|
|
if (new_state == currentState)
|
|
return currentState;
|
|
|
|
//
|
|
//-------------------------------------------------------------------
|
|
// Once destroyed, always destroyed until we tell it to never execute
|
|
//-------------------------------------------------------------------
|
|
//
|
|
if (currentState == DestroyedState && new_state != NeverExecuteState)
|
|
return currentState;
|
|
|
|
//
|
|
//---------------------------------------------------------------------
|
|
// State engine is being called directly here because we don't want the
|
|
// automatic execute/not-execute functionality in entity to operate.
|
|
// What we want is for destroyed state to destroy the subsystem, never
|
|
// execute state to turn it off, and anything else to turn it on
|
|
//---------------------------------------------------------------------
|
|
//
|
|
Subsystem *subsystem;
|
|
subsystem = Cast_Object(Subsystem *, owningEntity);
|
|
switch (BaseClass::BaseClass::RequestState(new_state, data))
|
|
{
|
|
case DestroyedState:
|
|
subsystem->DestroySubsystem();
|
|
break;
|
|
|
|
case NeverExecuteState:
|
|
if (oldState != NeverExecuteState)
|
|
owningEntity->EnterNeverExecuteState();
|
|
break;
|
|
|
|
default:
|
|
if (oldState == NeverExecuteState)
|
|
owningEntity->LeaveNeverExecuteState();
|
|
break;
|
|
}
|
|
|
|
return currentState;
|
|
}
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Subsystem::ExecutionStateEngine::TestInstance() const
|
|
{
|
|
Verify(IsDerivedFrom(DefaultData));
|
|
}
|
|
|
|
|
|
//#############################################################################
|
|
//############################### Subsystem ##############################
|
|
//#############################################################################
|
|
|
|
Subsystem::ClassData*
|
|
Subsystem::DefaultData = NULL;
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Subsystem::InitializeClass()
|
|
{
|
|
Verify(!DefaultData);
|
|
DefaultData =
|
|
new ClassData(
|
|
SubsystemClassID,
|
|
"MechWarrior4::Subsystem",
|
|
Entity::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);
|
|
|
|
CUSTOM_DIRECT_GAME_MODEL_ATTRIBUTE(
|
|
Subsystem__GameModel,
|
|
TotalSlotsTaken,
|
|
totalSlotsTaken,
|
|
int,
|
|
IntClassID
|
|
);
|
|
|
|
DIRECT_GAME_MODEL_ATTRIBUTE(
|
|
Subsystem__GameModel,
|
|
Tonage,
|
|
tonage,
|
|
Scalar
|
|
);
|
|
|
|
CUSTOM_DIRECT_GAME_MODEL_ATTRIBUTE(
|
|
Subsystem__GameModel,
|
|
BattleValue,
|
|
battleValue,
|
|
int,
|
|
IntClassID
|
|
);
|
|
|
|
CUSTOM_DIRECT_GAME_MODEL_ATTRIBUTE(
|
|
Subsystem__GameModel,
|
|
ItemID,
|
|
itemID,
|
|
int,
|
|
IntClassID
|
|
);
|
|
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Subsystem::TerminateClass()
|
|
{
|
|
Unregister_Object(DefaultData);
|
|
delete DefaultData;
|
|
DefaultData = NULL;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
Subsystem*
|
|
Subsystem::Make(
|
|
CreateMessage *message,
|
|
ReplicatorID *base_id
|
|
)
|
|
{
|
|
Check_Object(message);
|
|
gos_PushCurrentHeap(Heap);
|
|
Subsystem *new_entity =
|
|
new Subsystem(DefaultData, message, base_id, NULL);
|
|
gos_PopCurrentHeap();
|
|
Check_Object(new_entity);
|
|
return new_entity;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
Replicator::CreateMessage*
|
|
Subsystem::SaveMakeMessage(MemoryStream *stream, ResourceFile *res_file)
|
|
{
|
|
Check_Object(this);
|
|
Check_Object(stream);
|
|
|
|
stream->AllocateBytes(sizeof(CreateMessage));
|
|
Entity::SaveMakeMessage(stream, res_file);
|
|
CreateMessage *message =
|
|
Cast_Pointer(CreateMessage*, stream->GetPointer());
|
|
message->messageLength = sizeof(*message);
|
|
|
|
message->subsystemIndex = subsystemIndex;
|
|
message->locationID = internalLocation;
|
|
message->criticalHitsTaken = critHitsTaken;
|
|
return message;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
Subsystem::Subsystem(
|
|
ClassData *class_data,
|
|
CreateMessage *message,
|
|
ReplicatorID *base_id,
|
|
ElementRenderer::Element *element
|
|
):
|
|
Entity(class_data, message, base_id, element)
|
|
{
|
|
Check_Pointer(this);
|
|
Check_Object(message);
|
|
|
|
subsystemIndex = message->subsystemIndex;
|
|
internalLocation = (char)message->locationID;
|
|
critHitsTaken = message->criticalHitsTaken;
|
|
parentAsVehicle = NULL;
|
|
containedInSlotType = -1;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
MWObject* Subsystem::GetParentVehicle()
|
|
{
|
|
Check_Object(GetParentEntity());
|
|
Verify(GetParentEntity()->IsDerivedFrom(MWObject::DefaultData));
|
|
return (MWObject *)GetParentEntity();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
Vehicle* Subsystem::GetParentAsVehicle()
|
|
{
|
|
return parentAsVehicle;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
Subsystem::~Subsystem()
|
|
{
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Subsystem::Reuse(
|
|
const CreateMessage *message,
|
|
ReplicatorID *base_id
|
|
)
|
|
{
|
|
Check_Object(this);
|
|
STOP(("Not implemented"));
|
|
Entity::Reuse(message, base_id);
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
//Must call this virtual function in the inherited method!!!!
|
|
bool
|
|
Subsystem::ConnectSubsystem()
|
|
{
|
|
Check_Object(this);
|
|
|
|
MWInternalDamageObject *internal_structure;
|
|
const GameModel *model = GetGameModel();
|
|
Check_Object(model);
|
|
|
|
MWObject *parent_vehicle;
|
|
parent_vehicle = GetParentVehicle();
|
|
Check_Object(parent_vehicle);
|
|
|
|
int old_level = Stuff::ArmorLevel;
|
|
if (Stuff::ArmorLevel == 4)
|
|
Stuff::ArmorLevel = 3;
|
|
|
|
|
|
|
|
if(parent_vehicle->IsDerivedFrom(Vehicle::DefaultData))
|
|
parentAsVehicle = Cast_Object(Vehicle *, parent_vehicle);
|
|
// cant call cast object due to armor level four
|
|
// we are in parent_vehicles contructor
|
|
// if we call that dynamic cast will fail
|
|
|
|
Stuff::ArmorLevel = old_level;
|
|
|
|
internal_structure = parent_vehicle->internalDamageObjects.Find(internalLocation);
|
|
|
|
if (internal_structure != 0)
|
|
{
|
|
Check_Object(internal_structure);
|
|
if(parent_vehicle->DoesHaveAvailableTonage(model->tonage))
|
|
{
|
|
if(internal_structure->AddSubsystem(this))
|
|
{
|
|
parent_vehicle->AddTonage(model->tonage);
|
|
parent_vehicle->AddBattleValue(model->battleValue);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Subsystem::Respawn(Entity::CreateMessage *message)
|
|
{
|
|
Check_Object(this);
|
|
Check_Object(message);
|
|
|
|
Entity::Respawn(message);
|
|
|
|
//If we were destroyed then we must re add ourselves!
|
|
MWObject *parent_vehicle;
|
|
parent_vehicle = GetParentVehicle();
|
|
Check_Object(parent_vehicle);
|
|
MWInternalDamageObject *internal_structure;
|
|
internal_structure = parent_vehicle->internalDamageObjects.Find(internalLocation);
|
|
Check_Object(internal_structure);
|
|
if(!internal_structure->subsystemChain.IsPlugMember(this))
|
|
{
|
|
internal_structure->subsystemChain.Add(this);
|
|
}
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
//
|
|
bool
|
|
Subsystem::DisconnectSubsystem()
|
|
{
|
|
Check_Object(this);
|
|
|
|
MWObject *parent_vehicle;
|
|
parent_vehicle = GetParentVehicle();
|
|
Check_Object(parent_vehicle);
|
|
const GameModel *model = GetGameModel();
|
|
Check_Object(model);
|
|
|
|
parent_vehicle->SubtractTonage(model->tonage);
|
|
parent_vehicle->AddBattleValue(-model->battleValue);
|
|
|
|
MWInternalDamageObject *zone = parent_vehicle->internalDamageObjects.Find(internalLocation);
|
|
if(zone)
|
|
{
|
|
zone->FreeAvailableSlots(containedInSlotType, model->totalSlotsTaken);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
bool
|
|
Subsystem::TakeCriticalHit()
|
|
{
|
|
Check_Object(this);
|
|
|
|
Check_Object(executionState);
|
|
|
|
executionState->RequestState(ExecutionStateEngine::DestroyedState);
|
|
return true;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Subsystem::DestroySubsystem()
|
|
{
|
|
Check_Object(this);
|
|
|
|
MWObject *parent_vehicle;
|
|
parent_vehicle = GetParentVehicle();
|
|
Check_Object(parent_vehicle);
|
|
const GameModel *model = GetGameModel();
|
|
Check_Object(model);
|
|
parent_vehicle->SubtractTonage(model->tonage);
|
|
parent_vehicle->SubtractBattleValue(model->battleValue);
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Subsystem::BecomeInteresting(bool render_me)
|
|
{
|
|
Check_Object(this);
|
|
LOAD_LOGIC("Become Interesting::Subsystem");
|
|
|
|
BaseClass::BecomeInteresting(render_me);
|
|
|
|
ElementRenderer::Element *element = GetElement();
|
|
Check_Object(element);
|
|
|
|
element->SetAlwaysCullMode();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Subsystem::CreateStream(ResourceID data_list, MemoryStream *stream)
|
|
{
|
|
Check_Pointer(stream);
|
|
|
|
stream->AllocateBytes(sizeof(Subsystem__CreateMessage));
|
|
|
|
Subsystem__CreateMessage *create_message = Cast_Pointer(
|
|
Subsystem__CreateMessage *, stream->GetPointer());
|
|
|
|
create_message->alignment = Entity::DefaultAlignment;
|
|
create_message->initialAge = 0.0f;
|
|
create_message->replicatorFlags = 0;
|
|
create_message->dataListID = data_list;
|
|
create_message->classID = GetClassIDFromDataListID(data_list);
|
|
create_message->localToParent = LinearMatrix4D::Identity;
|
|
create_message->executionState.currentState = ExecutionStateEngine::NeverExecuteState;
|
|
create_message->nameID = NameTable::NullObjectID;
|
|
create_message->replicatorID.connectionID = 0;
|
|
create_message->replicatorID.localID = 1;
|
|
create_message->subsystemIndex = 0;
|
|
create_message->criticalHitsTaken = 0;
|
|
//This should be updated elsewhere.
|
|
create_message->locationID = 0;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Subsystem::TestInstance() const
|
|
{
|
|
Verify(IsDerivedFrom(DefaultData));
|
|
}
|
|
|