//===========================================================================// // 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 //############################################################################# //############################### 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 damage_iterator(&parent->damageObjects); SortedChainIteratorOf 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= 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)); }