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

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));
}