//===========================================================================// // File: DamageObject.cpp // // Project: Adept Brick: DamageObject // //---------------------------------------------------------------------------// // Date Who Modification // // -------- --- ---------------------------------------------------------- // // 03/08/99 DPB Created File //---------------------------------------------------------------------------// // Copyright (C) 1994-1995, Virtual World Entertainment, Inc. // // PROPRIETARY AND CONFIDENTIAL // //===========================================================================// #include "AdeptHeaders.hpp" #include "DamageObject.hpp" #include "Entity.hpp" #include "Tool.hpp" #include "Effect.hpp" #include "Map.hpp" #include "NameTable.hpp" #include "Site.hpp" //############################################################################# //########################### DamageObject ############################### //############################################################################# extern int g_nMR; DamageObject::ClassData* DamageObject::DefaultData = NULL; HGOSHEAP Adept::g_DamageObjectHeap = NULL; void __cdecl Adept::AdeptDamageSecurityCheckStart(void) { _asm { nop } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void DamageObject::InitializeClass() { Verify(!DefaultData); DefaultData = new ClassData( DamageObjectClassID, "Adept::DamageObject", Receiver::DefaultData, 0, NULL ); Check_Object(DefaultData); Verify(!g_DamageObjectHeap); g_DamageObjectHeap = gos_CreateMemoryHeap("DamageObject"); Check_Pointer(g_DamageObjectHeap); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void DamageObject::TerminateClass() { Check_Object(DefaultData); delete DefaultData; DefaultData = NULL; Check_Pointer(g_DamageObjectHeap); gos_DestroyMemoryHeap(g_DamageObjectHeap); g_DamageObjectHeap = NULL; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // DamageObject::DamageObject( ClassData *class_data, MemoryStream *stream ): Receiver(class_data) { Check_Pointer(this); Check_Object(stream); //Need to stream stuff in here *stream >> baseArmorValue; *stream >> currentArmorValue; *stream >> scaleSplashDamage; *stream >> damageObjectName; *stream >> internalDamageObjectID; *stream >> armorZone; *stream >> damageLevel; *stream >> m_armorType; *stream >> maxArmorValue; *stream >> m_specialConnectedToZone; armorLevel = 1.0f; damageArrayEntry = NULL; armorArrayEntry = NULL; internalDamageObject = NULL; {for(int qqq = 0; qqq < DmgModifier_Count; qqq++) { m_aDamageModifier[qqq * 2] = 1.0f; // real value m_aDamageModifier[qqq * 2 + 1] = 1.0f; // backup value }} m_HitPointPointer = NULL; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void* DamageObject::operator new(size_t size) { /* gos_PushCurrentHeap(g_DamageObjectHeap); void *temp = ::new(size_t); gos_PopCurrentHeap(); return temp; */ return gos_Malloc(size,g_DamageObjectHeap); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void DamageObject::Reuse() { Check_Object(this); m_HitPointPointer = NULL; currentArmorValue = baseArmorValue; damageLevel = NoArmorDamage; if(damageArrayEntry) *damageArrayEntry = damageLevel; Scalar damage_percentage; damage_percentage = 1.0f; armorLevel = damage_percentage; if (armorArrayEntry) *armorArrayEntry = damage_percentage; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void DamageObject::ConstructDamageObjectStream( MemoryStream *stream, Page *page ) { Check_Object(stream); Check_Object(page); int internal_damage_zone_id; int armor_zone; const char *armor_zone_text = ""; page->GetEntry("ArmorZone", &armor_zone_text); armor_zone = ArmorZoneTextToAscii(armor_zone_text); const char *zone_name; page->GetEntry("InternalDamageZone", &zone_name, true); internal_damage_zone_id = InternalDamageObject::InternalZoneTextToAscii(zone_name); const char *attached_zone_string; int attached_to_zone = NullArmorZone; if(page->GetEntry("SpecialAttachedToZone", &attached_zone_string)) attached_to_zone = ArmorZoneTextToAscii(attached_zone_string); Stuff::Scalar base_armor_value, current_armor_value, scale_splash_damage, max_armor_value; base_armor_value = 0.0f; page->GetEntry("BaseArmorValue", &base_armor_value); Verify(base_armor_value >= 0.0f); max_armor_value = base_armor_value; page->GetEntry("MaxArmorValue", &max_armor_value); Verify(max_armor_value > 0.0f); if (!page->GetEntry("CurrentArmorValue", ¤t_armor_value)) { current_armor_value = base_armor_value; } if (!page->GetEntry("ScaleSplashDamage", &scale_splash_damage)) { scale_splash_damage = 1.0f; } int damage_level = NoArmorDamage; RegisteredClass::ClassID class_id = DefaultData->GetClassID(); MString damage_object_name = page->GetName(); int armor_type = DefaultArmorType; *stream << class_id; *stream << base_armor_value; *stream << current_armor_value; *stream << scale_splash_damage; *stream << damage_object_name; *stream << internal_damage_zone_id; *stream << armor_zone; *stream << damage_level; *stream << armor_type; *stream << max_armor_value; *stream << attached_to_zone; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void DamageObject::Save(MemoryStream *stream) { Check_Object(this); RegisteredClass::ClassID class_id = DefaultData->GetClassID(); *stream << class_id; *stream << baseArmorValue; *stream << currentArmorValue; *stream << damageObjectName; *stream << internalDamageObjectID; *stream << armorZone; *stream << damageLevel; *stream << m_armorType; *stream << maxArmorValue; *stream << m_specialConnectedToZone; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // DamageObject::~DamageObject() { } // from here by jcem Stuff::Scalar DamageObject::GetDamageModifier (int nIdx) const { gosASSERT((0 <= nIdx) && (nIdx < DmgModifier_Count * 2)); return m_aDamageModifier[nIdx]; } void DamageObject::SetDamageModifier (int nIdx, Stuff::Scalar newvalue) { gosASSERT((0 <= nIdx) && (nIdx < DmgModifier_Count * 2)); m_aDamageModifier[nIdx] = newvalue; } // to here by jcem //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void DamageObject::Initialize(Entity *entity) { Check_Object(this); Check_Object(entity); parentEntity = entity; DetermineDamageLevel(); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // int DamageObject::DetermineDamageLevel() { Check_Object(this); int damage_level = NoArmorDamage; Scalar damage_percentage; if (baseArmorValue == 0) damage_percentage = 0; else damage_percentage = currentArmorValue / baseArmorValue; if(damage_percentage < 0.01f) { if(internalDamageObject) { if(internalDamageObject->currentInternalDamage <= 0.0f) { return InternalDestroyedDamage; } } damage_level = ArmorDestroyedDamage; } else if(damage_percentage < 0.1f) damage_level = BadArmorDamage; else if(damage_percentage < 0.5f) damage_level = MediumArmorDamage; else if(damage_percentage < 0.75f) damage_level = PartialArmorDamage; return damage_level; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void DamageObject::TakeDamage(Entity__TakeDamageMessage *message, Entity *entity_hit) { Check_Object(this); Check_Object(parentEntity); Scalar internal_damage_taken = 0.0f; // from here by jcem if (message->typeOfDamage == SplashDamageType) message->amountOfDamage *= m_aDamageModifier[DmgModifier_Splash * 2]; else // to here by jcem message->amountOfDamage *= m_aDamageModifier[DmgModifier_Default * 2]; // MSL 5.05 Advance Mode if (message->amountOfModifier) { message->amountOfDamage *= 0.5f; } if (message->amountOfDamage < 0.1f) message->amountOfDamage = 0.1f; parentEntity->ReactToHit(message,this); if (entity_hit != NULL) { entity_hit->ApplyVisualDamage(message, armorZone); } if (g_nMR == 2) { Stuff::Scalar amount_of_damage = message->amountOfDamage; if (message->typeOfDamage == SplashDamageType) { amount_of_damage *= scaleSplashDamage; } if (GetCurrentDamageLevel() == Destroyed) { //internal_damage_taken = amount_of_damage; internalDamageObject->TakeDamage(amount_of_damage/*internal_damage_taken*/, message); } parentEntity->ReactToDamageTaken(amount_of_damage/*damage_actually_taken*/,message->inflictingEntityID,message->weaponID,(int)message->typeOfDamage); return; } // if we are the server deal the damage... // FUTURE UPGRADES!!!!!! // If we are an instant weapon (beam) on the server take the damage // Anything else gets the info stuck in a damage accumulator. // That accumulator gets sent now and again. if (Network::GetInstance()->AmIServer()) { Stuff::Scalar amount_of_damage = message->amountOfDamage; //only apply this if there is armor left // MSL 5.03 Damage vs Armor Type Modified // MSL 5.04 Penalty reduced from .667 to .333 // MSL 5.05 Corrected Formulas and changed to 40/40 reduction/penalty if(currentArmorValue > 0) { if (m_armorType == ReflectiveArmor) { if(message->typeOfDamage == BeamDamageType) { amount_of_damage = amount_of_damage - (amount_of_damage * 0.4f); } if(message->typeOfDamage == ProjectileDamageType) { amount_of_damage = amount_of_damage + (amount_of_damage * 0.4f); } if(message->typeOfDamage == MissileDamageType) { amount_of_damage = amount_of_damage + (amount_of_damage * 0.4f); } } if (m_armorType == ReactiveArmor) { if(message->typeOfDamage == ProjectileDamageType) { amount_of_damage = amount_of_damage - (amount_of_damage * 0.4f); } if(message->typeOfDamage == MissileDamageType) { amount_of_damage = amount_of_damage - (amount_of_damage * 0.4f); } if(message->typeOfDamage == BeamDamageType) { amount_of_damage = amount_of_damage + (amount_of_damage * 0.4f); } } if (m_armorType == SolarianArmor) { if(message->typeOfDamage == ProjectileDamageType) { amount_of_damage = amount_of_damage - (amount_of_damage * 0.75f); } if(message->typeOfDamage == MissileDamageType) { amount_of_damage = amount_of_damage - (amount_of_damage * 0.75f); } if(message->typeOfDamage == BeamDamageType) { amount_of_damage = amount_of_damage - (amount_of_damage * 0.75f); } } } if(currentArmorValue == baseArmorValue) { internalDamageObject->m_firstHitTime = gos_GetElapsedTime() + 1.0f; } if (message->typeOfDamage == SplashDamageType) { amount_of_damage *= scaleSplashDamage; } Stuff::Scalar damage_actually_taken = amount_of_damage; if(currentArmorValue > 0.0f) { currentArmorValue -= amount_of_damage; if (m_HitPointPointer) { *m_HitPointPointer -= amount_of_damage; } if(currentArmorValue < 0.0f) { damage_actually_taken += currentArmorValue; internal_damage_taken = 0.0f - currentArmorValue; if (m_HitPointPointer) { *m_HitPointPointer += internal_damage_taken; // will be removed in internal damage object } currentArmorValue = 0.0f; } } else { internal_damage_taken = amount_of_damage; } if (internal_damage_taken > 0.0f) { Check_Object(internalDamageObject); damage_actually_taken += internalDamageObject->TakeDamage(internal_damage_taken, message); } parentEntity->ReactToDamageTaken(damage_actually_taken,message->inflictingEntityID,message->weaponID,(int)message->typeOfDamage); } else { Stuff::Scalar amount_of_damage = message->amountOfDamage; if (message->typeOfDamage == SplashDamageType) { amount_of_damage *= scaleSplashDamage; } if (GetCurrentDamageLevel() == Destroyed) { internal_damage_taken = amount_of_damage; internalDamageObject->TakeDamage(internal_damage_taken, message); } } RefrehDamageState(); // jcem } void DamageObject::RefrehDamageState() { // jcem - Recalc/Refresh current DamageLevel/damageArrayEntry/armorLevel/armorArrayEntry // this will be called after the modification of (currentArmorValue, internalDamageObject->currentInternalDamage...) damageLevel = DetermineDamageLevel(); if(damageArrayEntry) { *damageArrayEntry = damageLevel; } Scalar damage_percentage; if(internalDamageObject) damage_percentage = (currentArmorValue+internalDamageObject->currentInternalDamage) / (baseArmorValue+internalDamageObject->baseInternalDamage); else damage_percentage = (currentArmorValue) / (baseArmorValue); armorLevel = damage_percentage; if (armorArrayEntry) *armorArrayEntry = damage_percentage; } void DamageObject::RepairDamage(Scalar fRepair) { Stuff::Scalar fValue; if (internalDamageObject) { fValue = internalDamageObject->baseInternalDamage * fRepair; internalDamageObject->currentInternalDamage += fValue; if (internalDamageObject->baseInternalDamage < internalDamageObject->currentInternalDamage) { fValue = internalDamageObject->currentInternalDamage - internalDamageObject->baseInternalDamage; internalDamageObject->currentInternalDamage = internalDamageObject->baseInternalDamage; } else { fValue = 0.0; } } else { fValue = 0.0; } fValue += (baseArmorValue * fRepair); currentArmorValue += fValue; if (baseArmorValue < currentArmorValue) currentArmorValue = baseArmorValue; RefrehDamageState(); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // int DamageObject::ArmorZoneTextToAscii(const char *armor_name) { if(!_stricmp(armor_name, "NullZone")) return NullArmorZone; else if(!_stricmp(armor_name, "LeftLeg")) return LeftLegArmorZone; else if(!_stricmp(armor_name, "RightLeg")) return RightLegArmorZone; else if(!_stricmp(armor_name, "LeftArm")) return LeftArmArmorZone; else if(!_stricmp(armor_name, "RightArm")) return RightArmArmorZone; else if(!_stricmp(armor_name, "RightTorso")) return RightTorsoArmorZone; else if(!_stricmp(armor_name, "LeftTorso")) return LeftTorsoArmorZone; else if(!_stricmp(armor_name, "CenterTorso")) return CenterTorsoArmorZone; else if(!_stricmp(armor_name, "CenterRearTorso")) return CenterRearTorsoArmorZone; else if(!_stricmp(armor_name, "Head")) return HeadArmorZone; else if(!_stricmp(armor_name, "Special1")) return SpecialArmorZone1; else if(!_stricmp(armor_name, "Special2")) return SpecialArmorZone2; return DefaultArmorZone; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // const char * DamageObject::ArmorZoneAsciiToText(int armor_zone) { switch(armor_zone) { case NullArmorZone: return "Null"; case LeftLegArmorZone: return "LeftLeg"; case RightLegArmorZone: return "RightLeg"; case LeftArmArmorZone: return "LeftArm"; case RightArmArmorZone: return "RightArm"; case RightTorsoArmorZone: return "RightTorso"; case LeftTorsoArmorZone: return "LeftTorso"; case CenterTorsoArmorZone: return "CenterTorso"; case CenterRearTorsoArmorZone: return "CenterRearTorso"; case HeadArmorZone: return "Head"; case SpecialArmorZone1: return "Special1"; case SpecialArmorZone2: return "Special2"; } return "Default"; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void DamageObject::SetType(int new_type) { Check_Object(this); // MSL 5.0 Armor // Verify(new_type >= FerroFiberusArmor); Verify(new_type >= StandardArmor); Verify(new_type < TypeCount); m_armorType = new_type; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void DamageObject::AppendArmor(Scalar additive_armor) { Check_Object(this); baseArmorValue += additive_armor; currentArmorValue += additive_armor; if (m_HitPointPointer) { *m_HitPointPointer += additive_armor; } Clamp(baseArmorValue, 1.0f, maxArmorValue); if (currentArmorValue > maxArmorValue) { if (m_HitPointPointer) { *m_HitPointPointer -= (currentArmorValue - maxArmorValue); } } Clamp(currentArmorValue, 1.0f, maxArmorValue); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // bool DamageObject::CanRepair() { Check_Object(this); Check_Object(internalDamageObject); bool value = false; if((internalDamageObject->GetCurrentDamageLevel() != InternalDamageObject::Destroyed) && (GetCurrentDamageLevel() != NoDamage)) { value = true; } return value; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void DamageObject::TestInstance() { Verify(IsDerivedFrom(DefaultData)); } //############################################################################# //########################### InternalDamageObject ####################### //############################################################################# InternalDamageObject::ClassData* InternalDamageObject::DefaultData = NULL; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void InternalDamageObject::InitializeClass() { Verify(!DefaultData); DefaultData = new ClassData( InternalDamageObjectClassID, "Adept::InternalDamageObject", Receiver::DefaultData, 0, NULL ); Check_Object(DefaultData); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void InternalDamageObject::TerminateClass() { Check_Object(DefaultData); delete DefaultData; DefaultData = NULL; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // InternalDamageObject::InternalDamageObject( ClassData *class_data, MemoryStream *stream ): Receiver(class_data), m_sites(NULL, true), // The same sites were being returned for each call. The iterators // are now seperated into a weapon port iterator and and eject port // iterator. Added this change to Vengeance, too --CBT 1/10/2002 m_weaponSiteIterator (&m_sites), m_ejectionSiteIterator (&m_sites) { Check_Pointer(this); Check_Object(stream); //Need to stream stuff in here *stream >> baseInternalDamage; *stream >> currentInternalDamage; *stream >> parentEntityName; *stream >> damageMode; *stream >> damageZone; *stream >> damagePropagationZoneID; *stream >> m_internalType; *stream >> m_specialConnectedTo; MemoryStreamIO_Read(stream, &damageEffects); // MSL 5.02 headshot if (damageMode == HeadShotDamageMode) { m_headShotFlag = true; // MSL 5.05 Damage Mode // damageMode = DestructionDamageMode; } else m_headShotFlag = false; // MSL 5.03 Gyro Hit if (damageMode == GyroHitDamageMode) { m_gyrohitFlag = true; // MSL 5.05 Damage Mode damageMode = DestructionDamageMode; } else m_gyrohitFlag = false; currentDamageEffectResourceID = ResourceID::Null; damagePropagationObject = NULL; parentEntity = NULL; currentDamageEffect = NULL; m_HitPointPointer = NULL; m_firstHitTime = gos_GetElapsedTime(); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void* InternalDamageObject::operator new(size_t size) { /* gos_PushCurrentHeap(g_DamageObjectHeap); void *temp = ::new(size_t); gos_PopCurrentHeap(); return temp; */ return gos_Malloc(size,g_DamageObjectHeap); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void InternalDamageObject::Reuse() { Check_Object(this); m_HitPointPointer = NULL; currentInternalDamage = baseInternalDamage; currentDamageEffectResourceID = ResourceID::Null; currentDamageEffect = NULL; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void InternalDamageObject::ConstructInternalDamageObjectStream( MemoryStream *stream, Page *page, RegisteredClass::ClassID class_id ) { Check_Object(stream); Check_Object(page); int damage_zone; int damage_prop_zone = NullZone; const char *internal_zone = ""; page->GetEntry("DamageZone", &internal_zone, true); damage_zone = InternalZoneTextToAscii(internal_zone); const char *zone_name; if(page->GetEntry("DamagePropagationZone",&zone_name)) damage_prop_zone = InternalZoneTextToAscii(zone_name); int attached_to = NullZone; const char *attached_to_string; if(page->GetEntry("SpecialAttachedToZone", &attached_to_string)) attached_to = InternalZoneTextToAscii(attached_to_string); const char *parent_name = "NullParent"; page->GetEntry("ParentEntityName", &parent_name); MString parent_entity_name = parent_name; int damage_mode; const char *mode_name = ""; page->GetEntry("DamageMode", &mode_name); damage_mode = DamageModeTextToAscii(mode_name); Stuff::Scalar base_internal_damage, current_internal_damage, base_armor_value, current_armor_value; page->GetEntry("BaseInternalDamage", &base_internal_damage, true); Verify(base_internal_damage > 0); if (!page->GetEntry("CurrentInternalDamage", ¤t_internal_damage)) { current_internal_damage = base_internal_damage; } base_armor_value = 0.0f; page->GetEntry("BaseArmorValue", &base_armor_value); Verify(base_armor_value >= 0.0f); if (!page->GetEntry("CurrentArmorValue", ¤t_armor_value)) { current_armor_value = base_armor_value; } // //--------------------------------------------- //Create the array of Damage Effect ResourceIDs //--------------------------------------------- // DynamicArrayOf damage_effects; ChainOf *list = page->MakeNoteChain("DamageEffect"); Check_Object(list); Page::NoteIterator notes(list); int entry_count = notes.GetSize(); Verify(entry_count >= 0); damage_effects.SetLength(entry_count); Note *note; int i; // // For each entry, make entry in damage effect array // for ( i = 0; (note = notes.ReadAndNext()) != NULL && i < entry_count; i++ ) { Check_Object(note); const char *entry_data; note->GetEntry(&entry_data); Check_Pointer(entry_data); MString entry_string; MString effect_file_name; entry_string = entry_data; effect_file_name = entry_string.GetNthToken(0, ","); Check_Object(Tool::Instance); Check_Object(ResourceManager::Instance); Resource effect_resource; Tool::Instance->ConstructDataList(&effect_resource, effect_file_name); Check_Object(&effect_resource); Verify(effect_resource.DoesResourceExist()); MString percent_armor_string; Scalar percent_armor; percent_armor_string = entry_string.GetNthToken(1, ","); sscanf( percent_armor_string, "%f ", &percent_armor ); damage_effects[i].resourceID = effect_resource.GetResourceID(); damage_effects[i].armorPercent = percent_armor; } Check_Object(list); delete list; int internal_type = DefaultInternalType; *stream << class_id; *stream << base_internal_damage; *stream << current_internal_damage; *stream << parent_entity_name; *stream << damage_mode; *stream << damage_zone; *stream << damage_prop_zone; *stream << internal_type; *stream << attached_to; MemoryStreamIO_Write(stream, &damage_effects); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void InternalDamageObject::Save(MemoryStream *stream, RegisteredClass::ClassID class_id) { Check_Object(this); *stream << class_id; *stream << baseInternalDamage; *stream << currentInternalDamage; *stream << parentEntityName; *stream << damageMode; *stream << damageZone; *stream << damagePropagationZoneID; *stream << m_internalType; *stream << m_specialConnectedTo; MemoryStreamIO_Write(stream, &damageEffects); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // InternalDamageObject::~InternalDamageObject() { } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void InternalDamageObject::Initialize(Entity *entity) { Check_Object(this); Check_Object(entity); parentEntity = entity; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Scalar InternalDamageObject::TakeDamage(Scalar damage_taken, Entity__TakeDamageMessage *message) { Check_Object(this); Check_Object(parentEntity); Stuff::Scalar damage_already_distributed = 0.0f; Scalar rv = 0; if (g_nMR == 2) { PlayDamageEffects(message); //parentEntity->ReactToInternalDamage(damageMode); return rv; } if (Network::GetInstance()->AmIServer()) { if (currentInternalDamage > 0.0f) { rv = damage_taken; if((damageMode == DestructionDamageMode) && (damageZone > NullZone) && (damageZone < VehicleHullZone) && (gos_GetElapsedTime() < m_firstHitTime) && (message->typeOfDamage != HighExplosiveDamageType)) { if (m_HitPointPointer) { *m_HitPointPointer -= damage_taken; } currentInternalDamage -= damage_taken; if (currentInternalDamage < 0.9f) { if (m_HitPointPointer) { *m_HitPointPointer += (0.9f - currentInternalDamage); } } Min_Clamp(currentInternalDamage, 0.09f); } else { // // If we've already used up some of the damage here, we don't want to propagate it // below. Note that since we check for currentInternalDamage being <= 0 below we know that // we only get there if damage_take > currentInternalDamage so it is safe to assume // currentInternalDamage is the amount of damage already taken. // damage_already_distributed = currentInternalDamage; if (m_HitPointPointer) { *m_HitPointPointer -= damage_taken; } currentInternalDamage -= damage_taken; } PlayDamageEffects(message); // MSL 5.02 Head Shot // MSL 5.03 Gyro Hit if (m_headShotFlag == true) parentEntity->ReactToInternalDamage(HeadShotDamageMode); else if (m_gyrohitFlag == true) parentEntity->ReactToInternalDamage(GyroHitDamageMode); else parentEntity->ReactToInternalDamage(damageMode); InflictCriticalHit(); } if(currentInternalDamage <= 0.0f) { if(damagePropagationObject) { Check_Object(damagePropagationObject); Scalar prop_amount = 0.0f; // MSL 5.05 Used to determine percentage of damage propagated. switch(m_internalType) { case StandardInternal: prop_amount = 0.85f; break; case EndoSteelInternal: prop_amount = 1.0f; break; } // MSL 5.05 If Advancemode then modify propagation damage if (message->amountOfModifier) { prop_amount *= 0.5f; } // // If two items point to each other they can cause recursion that we don't want // when both become destroyed. The legs pointing at each other causes this // // This solution won't work if they ever put in second order propagation, ie... // lleg -> rleg (if destroyed) -> centertorso, or lleg->ltorso->ctorso // The first case isn't supported by the code, and the second won't work once the // ltorso is destroyed. The damage won't get to the center torso if (damagePropagationObject->currentInternalDamage > 0.0f) { rv += damagePropagationObject->TakeDamage((Scalar)((damage_taken - damage_already_distributed) * prop_amount), message); } } if(!parentEntity->IsDestroyed()) parentEntity->ReactToDestruction(damageMode, message->typeOfDamage); } } else { // create the effects PlayDamageEffects(message); // MSL 5.02 Head Shot // MSL 5.03 Gyro Hit if (m_headShotFlag == true) parentEntity->ReactToInternalDamage(HeadShotDamageMode); else if (m_gyrohitFlag == true) parentEntity->ReactToInternalDamage(GyroHitDamageMode); else parentEntity->ReactToInternalDamage(damageMode); } return (rv); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void InternalDamageObject::PlayDamageEffects(Entity__TakeDamageMessage *message) { Check_Object(this); Check_Object(message); if (message->quickDamage) // boardgame mode hit return; ResourceID want_to_play_effect; want_to_play_effect = GetDamageEffectID(); if((currentDamageEffectResourceID == want_to_play_effect) || (want_to_play_effect == ResourceID::Null)) { return; } currentDamageEffectResourceID = want_to_play_effect; ClassID class_ID; class_ID = Entity::GetClassIDFromDataListID(currentDamageEffectResourceID); Verify(class_ID != NullClassID); Effect::CreateMessage effect_create_message( sizeof(Effect::CreateMessage), DefaultEventPriority, Entity::CreateMessage::DefaultFlags, class_ID, Effect::DefaultFlags, currentDamageEffectResourceID, LinearMatrix4D::Identity, 0.0f, Entity::ExecutionStateEngine::AlwaysExecuteState, NameTable::NullObjectID, Entity::DefaultAlignment ); MemoryStream stream(&effect_create_message, effect_create_message.messageLength); Entity *entity; ReplicatorID effect_id = Connection::Hermit->GetNextReplicatorID(); entity = Entity::CreateEntity(&stream, &effect_id, false); Check_Object(entity); currentDamageEffect = Cast_Object(Effect *, entity); Check_Object(Map::GetInstance()); Map::GetInstance()->AddChild(entity); LinearMatrix4D effect_position = LinearMatrix4D::Identity; // effect_position.AlignAxis(message->normal, Y_Axis, X_Axis, Z_Axis); effect_position.BuildTranslation(message->hitOffset); LinearMatrix4D effect_offset = LinearMatrix4D::Identity; Point3D effect_offset_position; Check_Object(parentEntity); effect_offset_position.MultiplyByInverse(message->hitOffset, parentEntity->GetLocalToWorld()); effect_offset.BuildTranslation(effect_offset_position); currentDamageEffect->SetFollowEntity(parentEntity); currentDamageEffect->effectOffset = effect_offset; entity->SetNewLocalToParent(effect_position); entity->SyncMatrices(true); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // ResourceID InternalDamageObject::GetDamageEffectID() { Check_Object(this); Scalar closest_armor = 1.0f; ResourceID return_id; return_id = ResourceID::Null; for (int i = 0; i < damageEffects.GetLength(); i++) { Scalar damage_num = damageEffects[i].armorPercent; if ((damage_num >= (currentInternalDamage / baseInternalDamage)) && (damage_num <= closest_armor)) { closest_armor = damage_num; return_id = damageEffects[i].resourceID; } } return return_id; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // int InternalDamageObject::GetCurrentDamageLevel() { if (currentInternalDamage == baseInternalDamage) { return NoDamage; } if (currentInternalDamage <= 0.0f) { return Destroyed; } else return Damaged; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void InternalDamageObject::SetCurrentDamageLevel(int level) { if (level != GetCurrentDamageLevel()) { switch(level) { case NoDamage: if (m_HitPointPointer) { *m_HitPointPointer += (baseInternalDamage - currentInternalDamage); } currentInternalDamage = baseInternalDamage; break; case Damaged: if (m_HitPointPointer) { *m_HitPointPointer += ((baseInternalDamage*0.5f) - currentInternalDamage); } currentInternalDamage = baseInternalDamage*0.5f; break; case Destroyed: if (m_HitPointPointer) { *m_HitPointPointer += (0-currentInternalDamage); } currentInternalDamage = 0.0f; //parentEntity->ReactToDestruction(InternalDamageObject::DestructionDamageMode, 0); parentEntity->ReactToDestruction(damageMode, 0); break; } } } // The same sites were being returned for each call. The iterators // are now seperated into a weapon port iterator and and eject port // iterator. Added this change to Vengeance, too --CBT 1/10/2002 Site* InternalDamageObject::GetNextWeaponSite() { Check_Object(this); Site *site = m_weaponSiteIterator.GetCurrent(); if(!site) { m_weaponSiteIterator.First(); site = m_weaponSiteIterator.GetCurrent(); } else { //you have previously retuned a site, begin on the next m_weaponSiteIterator.Next(); if(!m_weaponSiteIterator.GetCurrent()) m_weaponSiteIterator.First(); site = m_weaponSiteIterator.GetCurrent(); } bool found = false; int num = 0; int total = m_sites.GetSize(); while((site) && (!found) && (num < total)) { Check_Object(site); char *name_lower = _strlwr((char *)site->GetName()); // CBT 1/24/02 - the mispelled string "missle" is included due to historical reasons if((strstr(name_lower, "gun")) || (strstr(name_lower, "missile")) || (strstr(name_lower, "missle")) || (strstr(name_lower, "torsoport"))) found = true; else { m_weaponSiteIterator.Next(); if(!m_weaponSiteIterator.GetCurrent()) m_weaponSiteIterator.First(); site = m_weaponSiteIterator.GetCurrent(); num ++; } } if(found) return site; return NULL; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Site* InternalDamageObject::GetNextEjectSite() { Site *site = m_ejectionSiteIterator.GetCurrent(); if(!site) { m_ejectionSiteIterator.First(); site = m_ejectionSiteIterator.GetCurrent(); } else { //you have previously returned a site, begin on the next m_ejectionSiteIterator.Next(); if(!m_ejectionSiteIterator.GetCurrent()) m_ejectionSiteIterator.First(); site = m_ejectionSiteIterator.GetCurrent(); } bool found = false; int num = 0; int total = m_sites.GetSize(); while((site) && (!found) && (num < total)) { Check_Object(site); char *name_lower = _strlwr((char *)site->GetName()); if(strstr(name_lower, "shellport")) found = true; else { m_ejectionSiteIterator.Next(); if(!m_ejectionSiteIterator.GetCurrent()) m_ejectionSiteIterator.First(); site = m_ejectionSiteIterator.GetCurrent(); num ++; } } if(found) return site; return NULL; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void InternalDamageObject::SilentDestruction() { if (m_HitPointPointer) { *m_HitPointPointer += (0-currentInternalDamage); } currentInternalDamage = 0.0f; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // int DamageObject::GetCurrentDamageLevel() { if (currentArmorValue == baseArmorValue) { return NoDamage; } if (currentArmorValue <= 0.0f) { return Destroyed; } else return Damaged; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void DamageObject::SetCurrentDamageLevel(int level) { if (level != GetCurrentDamageLevel()) { switch(level) { case NoDamage: if (m_HitPointPointer) { *m_HitPointPointer += (baseArmorValue - currentArmorValue); } currentArmorValue = baseArmorValue; break; case Damaged: if (m_HitPointPointer) { *m_HitPointPointer += ((baseArmorValue*0.5f) - currentArmorValue); } currentArmorValue = baseArmorValue*0.5f; break; case Destroyed: if (m_HitPointPointer) { *m_HitPointPointer += (0 - currentArmorValue); } currentArmorValue = 0.0f; break; } } damageLevel = DetermineDamageLevel(); if(damageArrayEntry) { *damageArrayEntry = damageLevel; } Scalar damage_percentage; if(internalDamageObject) damage_percentage = (currentArmorValue+internalDamageObject->currentInternalDamage) / (baseArmorValue+internalDamageObject->baseInternalDamage); else damage_percentage = (currentArmorValue) / (baseArmorValue); armorLevel = damage_percentage; if (armorArrayEntry) *armorArrayEntry = damage_percentage; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Fix Bug #3100 // Damage Object are now refreshed when a mech sustains internal damage void DamageObject::RefreshCurrentDamageLevel() { damageLevel = DetermineDamageLevel(); if(damageArrayEntry) { *damageArrayEntry = damageLevel; } Scalar damage_percentage; if(internalDamageObject) damage_percentage = (currentArmorValue+internalDamageObject->currentInternalDamage) / (baseArmorValue+internalDamageObject->baseInternalDamage); else damage_percentage = (currentArmorValue) / (baseArmorValue); armorLevel = damage_percentage; if (armorArrayEntry) *armorArrayEntry = damage_percentage; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void DamageObject::SilentDestruction() { if (m_HitPointPointer) { *m_HitPointPointer += (0 - currentArmorValue); } currentArmorValue = 0.0f; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Scalar DamageObject::GetHighResDamageLevel() { Scalar damage_level = currentArmorValue/baseArmorValue; Clamp(damage_level, 0.0f, 1.0f); return damage_level; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void DamageObject::SetHighResDamageLevel(Scalar damage_percentage) { if (m_HitPointPointer) { *m_HitPointPointer += ((baseArmorValue * damage_percentage) - currentArmorValue); } currentArmorValue = baseArmorValue * damage_percentage; damageLevel = DetermineDamageLevel(); if(damageArrayEntry) { *damageArrayEntry = damageLevel; } Scalar fred; if(internalDamageObject) fred = (currentArmorValue+internalDamageObject->currentInternalDamage) / (baseArmorValue+internalDamageObject->baseInternalDamage); else fred = (currentArmorValue) / (baseArmorValue); armorLevel = fred; if (armorArrayEntry) *armorArrayEntry = fred; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Scalar InternalDamageObject::GetHighResDamageLevel() { Scalar damage_level = currentInternalDamage/baseInternalDamage; Clamp(damage_level, 0.0f, 1.0f); return damage_level; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void InternalDamageObject::SetHighResDamageLevel(Scalar damage_percentage) { if (GetCurrentDamageLevel() != Destroyed) { if (m_HitPointPointer) { *m_HitPointPointer += ((baseInternalDamage*damage_percentage)-currentInternalDamage); } currentInternalDamage = baseInternalDamage*damage_percentage; if (damage_percentage == 0.0f) { if (m_HitPointPointer) { *m_HitPointPointer += (0-currentInternalDamage); } currentInternalDamage = 0.0f; //parentEntity->ReactToDestruction(InternalDamageObject::DestructionDamageMode, 0); parentEntity->ReactToDestruction(damageMode, 0); } } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // int InternalDamageObject::DamageModeTextToAscii(const char *mode_name) { if(!_stricmp(mode_name, "GimpLeftDamageMode")) return GimpLeftDamageMode; else if(!_stricmp(mode_name, "GimpRightDamageMode")) return GimpRightDamageMode; else if(!_stricmp(mode_name, "DestructionDamageMode")) return DestructionDamageMode; // MSL 5.02 headshot else if (!_stricmp(mode_name, "HeadShotDamageMode")) return HeadShotDamageMode; // MSL 5.03 Gyro Hit else if (!_stricmp(mode_name, "GyroHitDamageMode")) return GyroHitDamageMode; else if(!_stricmp(mode_name, "DetachableDamageMode")) return DetachableDamageMode; // MSL 5.02 Engine else if(!_stricmp(mode_name, "EngineDamageMode")) return EngineDamageMode; // MSL 5.05 Torso else if(!_stricmp(mode_name, "TorsoLeftDamageMode")) return TorsoLeftDamageMode; else if(!_stricmp(mode_name, "TorsoRightDamageMode")) return TorsoRightDamageMode; else if(!_stricmp(mode_name, "ArmLeftDamageMode")) return ArmLeftDamageMode; else if(!_stricmp(mode_name, "ArmRightDamageMode")) return ArmRightDamageMode; return GeneralDamageMode; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // const char * InternalDamageObject::DamageModeAsciiToText(int damage_mode) { switch(damage_mode) { case GimpLeftDamageMode: return "GimpLeftDamageMode"; case GimpRightDamageMode: return "GimpRightDamageMode"; case DestructionDamageMode: return "DestructionDamageMode"; // MSL 5.02 headshot case HeadShotDamageMode: return "HeadShotDamageMode"; // MSL 5.03 Gyro Hit case GyroHitDamageMode: return "GyroHitDamageMode"; case DetachableDamageMode: return "DetachableDamageMode"; case EngineDamageMode: return "EngineDamageMode"; // MSL 5.05 Torso case TorsoLeftDamageMode: return "TorsoLeftDamageMode"; case TorsoRightDamageMode: return "TorsoRightDamageMode"; case ArmLeftDamageMode: return "ArmLeftDamageMode"; case ArmRightDamageMode: return "ArmRightDamageMode"; } return "GeneralDamageMode"; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // int InternalDamageObject::InternalZoneTextToAscii(const char *mode_name) { if(!_stricmp(mode_name, "NullZone")) return NullZone; else if(!_stricmp(mode_name, "LeftLeg")) return LeftLegZone; else if(!_stricmp(mode_name, "RightLeg")) return RightLegZone; else if(!_stricmp(mode_name, "LeftArm")) return LeftArmZone; else if(!_stricmp(mode_name, "RightArm")) return RightArmZone; else if(!_stricmp(mode_name, "RightTorso")) return RightTorsoZone; else if(!_stricmp(mode_name, "LeftTorso")) return LeftTorsoZone; else if(!_stricmp(mode_name, "CenterTorso")) return CenterTorsoZone; else if(!_stricmp(mode_name, "Head")) return HeadZone; else if(!_stricmp(mode_name, "Special1")) return SpecialZone1; else if(!_stricmp(mode_name, "Special2")) return SpecialZone2; else if(!_stricmp(mode_name, "VehicleHull")) return VehicleHullZone; else if(!_stricmp(mode_name, "VehicleWeapon")) return VehicleWeaponZone; else if(!_stricmp(mode_name, "VehicleSpecial")) return VehicleSpecialZone; return DefaultZone; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // const char * InternalDamageObject::InternalZoneAsciiToText(int internal_zone) { switch(internal_zone) { case NullZone: return "Null"; case LeftLegZone: return "LeftLeg"; case RightLegZone: return "RightLeg"; case LeftArmZone: return "LeftArm"; case RightArmZone: return "RightArm"; case RightTorsoZone: return "RightTorso"; case LeftTorsoZone: return "LeftTorso"; case CenterTorsoZone: return "CenterTorso"; case HeadZone: return "Head"; case SpecialZone1: return "Special1"; case SpecialZone2: return "Special2"; case VehicleHullZone: return "VehicleHull"; case VehicleWeaponZone: return "VehicleWeapon"; case VehicleSpecialZone: return "VehicleSpecial"; } return "Default"; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void InternalDamageObject::SetType(int new_type) { Check_Object(this); Verify(new_type >= StandardInternal); Verify(new_type < TypeCount); m_internalType = new_type; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void InternalDamageObject::TestInstance() { Verify(IsDerivedFrom(DefaultData)); } void __cdecl Adept::AdeptDamageSecurityCheckStop(void) { _asm { nop } }