Files
firestorm/Gameleap/code/mw4/Code/MW4/Armor.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

634 lines
19 KiB
C++

//===========================================================================//
// File: Armor.cpp //
//---------------------------------------------------------------------------//
// Date Who Modification //
// -------- --- ---------------------------------------------------------- //
// 12/19/01 MSL Added StandardArmor //
//---------------------------------------------------------------------------//
// Copyright (C) 1998, Fasa Interactive //
// All Rights reserved worldwide //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL //
//===========================================================================//
#include "MW4Headers.hpp"
#include "Armor.hpp"
#include "Mech.hpp"
#include "MWDamageObject.hpp"
#include "SubsystemClassData.hpp"
#include <Adept\DamageObject.hpp>
//#############################################################################
//############################### Armor #################################
//#############################################################################
Armor::ClassData*
Armor::DefaultData = NULL;
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
Armor::InitializeClass()
{
Verify(!DefaultData);
DefaultData =
new ClassData(
ArmorClassID,
"MechWarrior4::Armor",
Subsystem::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(
Armor__GameModel,
ArmDistributive,
m_armDistributive,
Scalar
);
DIRECT_GAME_MODEL_ATTRIBUTE(
Armor__GameModel,
LegDistributive,
m_legDistributive,
Scalar
);
DIRECT_GAME_MODEL_ATTRIBUTE(
Armor__GameModel,
SideFrontDistributive,
m_sideFrontDistributive,
Scalar
);
DIRECT_GAME_MODEL_ATTRIBUTE(
Armor__GameModel,
CenterFrontDistributive,
m_centerFrontDistributive,
Scalar
);
DIRECT_GAME_MODEL_ATTRIBUTE(
Armor__GameModel,
CenterRearDistributive,
m_centerRearDistributive,
Scalar
);
// MSL 5.00 Armor
CUSTOM_DIRECT_GAME_MODEL_ATTRIBUTE(
Armor__GameModel,
PointsPerStandardTon,
m_pointsPerStandardTon,
int,
IntClassID
);
CUSTOM_DIRECT_GAME_MODEL_ATTRIBUTE(
Armor__GameModel,
PointsPerFerroTon,
m_pointsPerFerroTon,
int,
IntClassID
);
CUSTOM_DIRECT_GAME_MODEL_ATTRIBUTE(
Armor__GameModel,
PointsPerReflectiveTon,
m_pointsPerReflectiveTon,
int,
IntClassID
);
CUSTOM_DIRECT_GAME_MODEL_ATTRIBUTE(
Armor__GameModel,
PointsPerReactiveTon,
m_pointsPerReactiveTon,
int,
IntClassID
);
CUSTOM_DIRECT_GAME_MODEL_ATTRIBUTE(
Armor__GameModel,
PointsPerSolarianTon,
m_pointsPerSolarianTon,
int,
IntClassID
);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
Armor::TerminateClass()
{
Unregister_Object(DefaultData);
delete DefaultData;
DefaultData = NULL;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Armor*
Armor::Make(
CreateMessage *message,
ReplicatorID *base_id
)
{
Check_Object(message);
gos_PushCurrentHeap(Heap);
Armor *new_entity =
new Armor(DefaultData, message, base_id, NULL);
gos_PopCurrentHeap();
Check_Object(new_entity);
return new_entity;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Replicator::CreateMessage*
Armor::SaveMakeMessage(MemoryStream *stream, ResourceFile *res_file)
{
Check_Object(this);
Check_Object(stream);
stream->AllocateBytes(sizeof(CreateMessage));
Subsystem::SaveMakeMessage(stream, res_file);
CreateMessage *message =
Cast_Pointer(CreateMessage*, stream->GetPointer());
message->messageLength = sizeof(*message);
message->m_leftLegArmor = m_armorZoneValues[DamageObject::LeftLegArmorZone];
message->m_rightLegArmor = m_armorZoneValues[DamageObject::RightLegArmorZone];
message->m_leftArmArmor = m_armorZoneValues[DamageObject::LeftArmArmorZone];
message->m_rightArmArmor = m_armorZoneValues[DamageObject::RightArmArmorZone];
message->m_leftFrontTorsoArmor = m_armorZoneValues[DamageObject::LeftTorsoArmorZone];
message->m_rightFrontTorsoArmor = m_armorZoneValues[DamageObject::RightTorsoArmorZone];
message->m_centerFrontTorsoArmor = m_armorZoneValues[DamageObject::CenterTorsoArmorZone];
message->m_centerRearTorsoArmor = m_armorZoneValues[DamageObject::CenterRearTorsoArmorZone];
message->m_headArmor = m_armorZoneValues[DamageObject::HeadArmorZone];
message->m_armorType = m_armorType;
message ->m_internalType = m_internalType;
return message;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Armor::Armor(
ClassData *class_data,
CreateMessage *message,
ReplicatorID *base_id,
ElementRenderer::Element *element
):
Subsystem(class_data, message, base_id, element)
{
Check_Pointer(this);
Check_Object(message);
//Set up the armor Zone array
m_totalArmor = 0;
m_armorZoneValues[DamageObject::LeftLegArmorZone] = message->m_leftLegArmor;
m_totalArmor += message->m_leftLegArmor;
m_armorZoneValues[DamageObject::RightLegArmorZone] = message->m_rightLegArmor;
m_totalArmor += message->m_rightLegArmor;
m_armorZoneValues[DamageObject::LeftArmArmorZone] = message->m_leftArmArmor;
m_totalArmor += message->m_leftArmArmor;
m_armorZoneValues[DamageObject::RightArmArmorZone] = message->m_rightArmArmor;
m_totalArmor += message->m_rightArmArmor;
m_armorZoneValues[DamageObject::RightTorsoArmorZone] = message->m_rightFrontTorsoArmor;
m_totalArmor += message->m_rightFrontTorsoArmor;
m_armorZoneValues[DamageObject::LeftTorsoArmorZone] = message->m_leftFrontTorsoArmor;
m_totalArmor += message->m_leftFrontTorsoArmor;
m_armorZoneValues[DamageObject::CenterTorsoArmorZone] = message->m_centerFrontTorsoArmor;
m_totalArmor += message->m_centerFrontTorsoArmor;
m_armorZoneValues[DamageObject::CenterRearTorsoArmorZone] = message->m_centerRearTorsoArmor;
m_totalArmor += message->m_centerRearTorsoArmor;
m_armorZoneValues[DamageObject::HeadArmorZone] = message->m_headArmor;
m_totalArmor += message->m_headArmor;
m_armorType = message->m_armorType,
m_internalType = message->m_internalType;
const GameModel *model = GetGameModel();
Check_Object(model);
m_armorMultiplier = 1;
switch(m_armorType)
{
// MSL 5.00 Armor
case StandardArmor:
m_armorMultiplier = model->m_pointsPerStandardTon;
break;
case FerroFiberusArmor:
m_armorMultiplier = model->m_pointsPerFerroTon;
break;
case ReactiveArmor:
m_armorMultiplier = model->m_pointsPerReactiveTon;
break;
case ReflectiveArmor:
m_armorMultiplier = model->m_pointsPerReflectiveTon;
break;
case SolarianArmor:
m_armorMultiplier = model->m_pointsPerSolarianTon;
break;
}
m_totalTonage =(m_totalArmor / m_armorMultiplier);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Armor::~Armor()
{
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
Armor::ConnectSubsystem()
{
InitializeDamageObjectsWithArmor();
GetParentVehicle()->AddTonage(m_totalTonage);
//Must call the inherited virtual function!!!!
return Subsystem::ConnectSubsystem();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
Armor::InitializeDamageObjectsWithArmor()
{
Check_Object(this);
MWObject *parent = GetParentVehicle();
Check_Object(parent);
DamageObject *damage_object;
MWInternalDamageObject *internal_object;
SortedChainIteratorOf<DamageObject *, int> damage_iterator(&parent->damageObjects);
SortedChainIteratorOf<MWInternalDamageObject *, int> internal_iterator(&parent->internalDamageObjects);
//
//---------------------------------------------------------------
//Setup the Damage Objects with Armor Type and their distribution
//---------------------------------------------------------------
//
for(int i=DamageObject::LeftLegArmorZone; i<=DamageObject::HeadArmorZone; i++)
{
damage_object = parent->damageObjects.Find(i);
if(damage_object)
{
damage_object->SetType(m_armorType);
damage_object->AppendArmor(m_armorZoneValues[i]);
}
#if defined(LAB_ONLY)
else
{
PAUSE(("This mech is missing a %s zone!", DamageObject::ArmorZoneAsciiToText(i)));
}
#endif
}
//
//------------------------------------
//Setup Internal Zones with their type
//------------------------------------
//
while((internal_object = internal_iterator.ReadAndNext()) != NULL)
{
Check_Object(internal_object);
internal_object->SetType(m_internalType);
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
Armor::ArmorTypeTextToAscii(const char *armor_type_string)
{
// MSL 5.00 Armor
if(!_stricmp(armor_type_string, "Standard"))
return StandardArmor;
if(!_stricmp(armor_type_string, "FerroFiberus"))
return FerroFiberusArmor;
if(!_stricmp(armor_type_string, "Reactive"))
return ReactiveArmor;
if(!_stricmp(armor_type_string, "Reflective"))
return ReflectiveArmor;
if(!_stricmp(armor_type_string, "Solarian"))
return SolarianArmor;
PAUSE(("That is a bad Armor String: %s", armor_type_string));
return -1;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
const char*
Armor::ArmorTypeAsciiToText(int armor_type)
{
switch(armor_type)
{
// MSL 5.00 Armor
case StandardArmor:
return "Standard";
case FerroFiberusArmor:
return "FerroFiberus";
case ReactiveArmor:
return "Reactive";
case ReflectiveArmor:
return "Reflective";
case SolarianArmor:
return "Solarian";
}
PAUSE(("That is a bad armor value"));
return "Bad";
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
Armor::InternalTypeTextToAscii(const char *internal_type_string)
{
if(!_stricmp(internal_type_string, "Standard"))
return StandardInternal;
if(!_stricmp(internal_type_string, "EndoSteel"))
return EndoSteelInternal;
PAUSE(("That is a bad Internal String: %s", internal_type_string));
return -1;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
const char*
Armor::InternalTypeAsciiToText(int internal_type)
{
switch(internal_type)
{
case StandardInternal:
return "Statndard";
case EndoSteelInternal:
return "EndoSteel";
}
PAUSE(("That is a bad armor value"));
return "Bad";
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
Armor::ApplyDistributiveArmor()
{
Check_Object(this);
//Ok so the easiest way to do this is for us...to just clear all the armor and then
//run through them and distribute it all.
Scalar total_armor = 0.0f;
Scalar left_over_armor = 0.0f;
Scalar armor_used = 0.0f; //use for armoring purposes (code armor that is)
for(int i=DamageObject::LeftLegArmorZone; i<=DamageObject::HeadArmorZone; i++)
{
total_armor += m_armorZoneValues[i];
m_armorZoneValues[i] = 0.0f;
}
const GameModel *model = GetGameModel();
Check_Object(model);
//We need to make the distribution array so we can easily run everything else through a loop
Scalar armor_dist[DamageObject::HeadArmorZone];
armor_dist[DamageObject::LeftLegArmorZone] = model->m_legDistributive;
armor_dist[DamageObject::RightLegArmorZone] = model->m_legDistributive;
armor_dist[DamageObject::LeftArmArmorZone] = model->m_armDistributive;
armor_dist[DamageObject::RightArmArmorZone] = model->m_armDistributive;
armor_dist[DamageObject::RightTorsoArmorZone] = model->m_sideFrontDistributive;
armor_dist[DamageObject::LeftTorsoArmorZone] = model->m_sideFrontDistributive;
armor_dist[DamageObject::CenterTorsoArmorZone] = model->m_centerFrontDistributive;
armor_dist[DamageObject::CenterRearTorsoArmorZone] = model->m_centerRearDistributive;
//first grab the head
//Ok now we run the loop and try to distribute the armor based upon our nice little array
//We should not have to worry at all about tonnage since the armor has already been added...we
//just need to redistribute the numbers!
MWObject *parent = GetParentVehicle();
Check_Object(parent);
//first grab the head and fill it
DamageObject *damage_object = parent->damageObjects.Find(DamageObject::HeadArmorZone);
Check_Object(damage_object);
m_armorZoneValues[DamageObject::HeadArmorZone] = damage_object->maxArmorValue;
total_armor -= damage_object->maxArmorValue;
for(i = DamageObject::LeftLegArmorZone; i<DamageObject::HeadArmorZone; i++)
{
Verify(armor_dist[i] >= 0.0f);
Scalar armor_amount = total_armor * armor_dist[i];
//Check the max on this one first...see if we have left_over
DamageObject *damage_object = parent->damageObjects.Find(i);
Check_Object(damage_object);
if(armor_amount > damage_object->maxArmorValue)
left_over_armor += armor_amount - damage_object->maxArmorValue;
Clamp(armor_amount, 0.0f, damage_object->maxArmorValue);
m_armorZoneValues[i] = armor_amount;
armor_used += armor_amount;
Verify(armor_used <= total_armor);
}
//now run through the left over and just place the shit!
for(i = DamageObject::HeadArmorZone - 1; i>=DamageObject::LeftLegArmorZone; i--)
{
DamageObject *damage_object = parent->damageObjects.Find(i);
Check_Object(damage_object);
Scalar armor_amount = left_over_armor;
//Try to add it all...just keep track of what we don't add.
if(left_over_armor > damage_object->maxArmorValue)
left_over_armor = left_over_armor - damage_object->maxArmorValue;
else
left_over_armor = 0.0f;
Clamp(armor_amount, 0.0f, damage_object->maxArmorValue);
m_armorZoneValues[i] += armor_amount;
armor_used += armor_amount;
Verify(armor_used <= total_armor);
}
Verify(armor_used = total_armor);
return true;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
Armor::ApplyZoneBasedArmor(int armor_zone, Stuff::Scalar armor_amount)
{
Check_Object(this);
MWObject *parent = GetParentVehicle();
Check_Object(parent);
DamageObject *damage_object;
damage_object = parent->damageObjects.Find(armor_zone);
if(!damage_object)
return false;
//these checks are needed so we know when to add tonnage
if(((m_armorZoneValues[armor_zone] + armor_amount * m_armorMultiplier) - damage_object->maxArmorValue) > SMALL)
{
Max_Clamp(m_armorZoneValues[armor_zone], damage_object->maxArmorValue);
return false;
}
if((m_armorZoneValues[armor_zone] + (armor_amount * m_armorMultiplier)) < 0.0f)
{
Min_Clamp(m_armorZoneValues[armor_zone], 1.0f);
return false;
}
if(GetParentVehicle()->DoesHaveAvailableTonage(armor_amount))
{
m_armorZoneValues[armor_zone] += (armor_amount * m_armorMultiplier);
Clamp(m_armorZoneValues[armor_zone], 0.0f, damage_object->maxArmorValue);
m_totalTonage += armor_amount;
GetParentVehicle()->AddTonage(armor_amount);
return true;
}
return false;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
Armor::SetZoneBasedArmor(int armor_zone, Stuff::Scalar armor_amount)
{
Check_Object(this);
Scalar amount_to_add = armor_amount - (m_armorZoneValues[armor_zone] / m_armorMultiplier);
return ApplyZoneBasedArmor(armor_zone, amount_to_add);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
Armor::SetArmorType(int new_type)
{
Check_Object(this);
const GameModel *model = GetGameModel();
Check_Object(model);
//The first thing that we need to do is to get all the values in tonnages so
//we can manipulate all the values with the armor change.
Scalar ratio = (Scalar)m_armorMultiplier;
switch(new_type)
{
// MSL 5.00 Armor
case StandardArmor:
m_armorMultiplier = model->m_pointsPerStandardTon;
break;
case FerroFiberusArmor:
m_armorMultiplier = model->m_pointsPerFerroTon;
break;
case ReactiveArmor:
m_armorMultiplier = model->m_pointsPerReactiveTon;
break;
case ReflectiveArmor:
m_armorMultiplier = model->m_pointsPerReflectiveTon;
break;
case SolarianArmor:
m_armorMultiplier = model->m_pointsPerSolarianTon;
break;
default:
return -1;
}
//i.e. if old was 20 per ton and new is 16 per ton....we want 16/20
ratio = m_armorMultiplier / ratio;
for(int i=DamageObject::LeftLegArmorZone; i<=DamageObject::HeadArmorZone; i++)
{
m_armorZoneValues[i] *= ratio;
}
if(ratio > 1.0f)
{
//This means that we may have over stepped our points bounds! We need to run through
//And check to make sure that this is not true
for(i=DamageObject::LeftLegArmorZone; i<=DamageObject::HeadArmorZone; i++)
{
DamageObject *damage_object;
damage_object = GetParentVehicle()->damageObjects.Find(i);
Check_Object(damage_object);
if(m_armorZoneValues[i] > damage_object->maxArmorValue)
{
//We have surpased our max and must clamp and release some tonnage!
Scalar overflow = m_armorZoneValues[i] - damage_object->maxArmorValue;
Scalar tonnage_overflow = overflow / m_armorMultiplier;
Max_Clamp(m_armorZoneValues[i], damage_object->maxArmorValue);
GetParentVehicle()->AddTonage(-tonnage_overflow);
}
}
}
m_armorType = new_type;
return 1;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
Armor::CreateStream(ResourceID data_list, MemoryStream *stream)
{
Check_Pointer(stream);
stream->AllocateBytes(sizeof(Armor__CreateMessage));
Subsystem::CreateStream(data_list, stream);
Armor__CreateMessage *create_message = Cast_Pointer(
Armor__CreateMessage *, stream->GetPointer());
create_message->m_leftLegArmor = 0.0f;
create_message->m_rightLegArmor = 0.0f;
create_message->m_leftArmArmor = 0.0f;
create_message->m_rightArmArmor = 0.0f;
create_message->m_leftFrontTorsoArmor = 0.0f;
create_message->m_rightFrontTorsoArmor = 0.0f;
create_message->m_centerFrontTorsoArmor = 0.0f;
create_message->m_centerRearTorsoArmor = 0.0f;
create_message->m_headArmor = 0.0f;
// MSL 5.00 Armor
// create_message->m_armorType = FerroFiberusArmor;
create_message->m_armorType = StandardArmor;
create_message->m_internalType = StandardInternal;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
Armor::TestInstance() const
{
Verify(IsDerivedFrom(DefaultData));
}