#include "MW4Headers.hpp" #include "AI_Weapons.hpp" #include "BeamWeapon.hpp" #include "ProjectileWeapon.hpp" #include "MWObject.hpp" #include "MWDamageObject.hpp" #include #include "aiutils.hpp" #include "bombastweapon.hpp" #include #include "SubsystemClassData.hpp" #include #include "Sensor.hpp" #include using namespace MW4AI; const Stuff::Scalar friendly_fire_stretch_distance = 10.0f; inline bool WeaponReadyToFire(MechWarrior4::Weapon& weapon, Stuff::Time min_delay, Stuff::Time max_delay) { Verify(min_delay >= 0); Verify(max_delay >= min_delay); Stuff::Time delay = ((max_delay - min_delay) * weapon.GetAIWaitValue()) + min_delay; return (gos_GetElapsedTime() >= weapon.GetLastTimeReadyToFire() + delay); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void MW4AI::SetWeaponsToMaxWaitValue(MechWarrior4::MWObject& object) { Stuff::ChainIteratorOf i(&(object.weaponChain)); MechWarrior4::Weapon* weapon = 0; while ((weapon = i.ReadAndNext()) != 0) { weapon->SetMaxWaitValue(); } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // GetMaxWeaponDistance(): gets the maximum distance of a weapon // Stuff::Scalar MW4AI::GetMaxWeaponDistance(MechWarrior4::Weapon& weapon) { if (weapon.IsDerivedFrom(MechWarrior4::BeamWeapon::DefaultData) == true) { MechWarrior4::BeamWeapon* beam = Cast_Object(MechWarrior4::BeamWeapon*,&weapon); return (beam->GetGameModel()->maxDistance); } if (weapon.IsDerivedFrom(MechWarrior4::ProjectileWeapon::DefaultData) == true) { MechWarrior4::ProjectileWeapon* projectile = Cast_Object(MechWarrior4::ProjectileWeapon*,&weapon); return (projectile->GetGameModel()->maxDistance); } Verify(!"Cannot get max range: weapon type not found."); return (0); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // GetMaxWeaponDistance(): gets the maximum distance of any of an object's weapons // Stuff::Scalar MW4AI::GetMaxWeaponDistance(MechWarrior4::MWObject& object) { Stuff::ChainIteratorOf i(&(object.weaponChain)); MechWarrior4::Weapon* weapon = 0; Stuff::Scalar max = 0; while ((weapon = i.ReadAndNext()) != 0) { Check_Object(weapon); Stuff::Scalar this_range = GetMaxWeaponDistance(*weapon); if (this_range > max) { max = this_range; } } return (max); } // MSL 5.03 Min Distance (Disabled) //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // GetMaxWeaponDistance(): gets the maximum distance of a weapon // //Stuff::Scalar MW4AI::GetMinWeaponDistance(MechWarrior4::Weapon& weapon) //{ // if (weapon.IsDerivedFrom(MechWarrior4::BeamWeapon::DefaultData) == true) // { // MechWarrior4::BeamWeapon* beam = Cast_Object(MechWarrior4::BeamWeapon*,&weapon); // return (beam->GetGameModel()->minDistance); // } // if (weapon.IsDerivedFrom(MechWarrior4::ProjectileWeapon::DefaultData) == true) // { // MechWarrior4::ProjectileWeapon* projectile = Cast_Object(MechWarrior4::ProjectileWeapon*,&weapon); // return (projectile->GetGameModel()->minDistance); // } // Verify(!"Cannot get min range: weapon type not found."); // return (0); //} //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // GetMaxWeaponDistance(): gets the maximum distance of any of an object's weapons // Stuff::Scalar MW4AI::GetMinWeaponDistance(MechWarrior4::MWObject& object) { Stuff::ChainIteratorOf i(&(object.weaponChain)); MechWarrior4::Weapon* weapon = 0; weapon = i.ReadAndNext(); if (weapon == 0) { return (0); } Check_Object(weapon); Stuff::Scalar min = GetMaxWeaponDistance(*weapon); while ((weapon = i.ReadAndNext()) != 0) { Check_Object(weapon); Stuff::Scalar this_range = GetMaxWeaponDistance(*weapon); if (this_range < min) { min = this_range; } } return (min); } unsigned int MW4AI::NumWeaponsReady(MechWarrior4::MWObject& vehicle, Stuff::Time min_delay, Stuff::Time max_delay) { Stuff::ChainIteratorOf i(&(vehicle.weaponChain)); MechWarrior4::Weapon* weapon = 0; unsigned int rv = 0; while ((weapon = i.ReadAndNext()) != 0) { Check_Object(weapon); if (WeaponReadyToFire(*weapon,min_delay,max_delay) == true) { ++rv; } } return (rv); } unsigned int MW4AI::NumWeapons(MechWarrior4::MWObject& vehicle) { Stuff::ChainIteratorOf i(&(vehicle.weaponChain)); unsigned int rv = 0; while (i.ReadAndNext() != 0) // here's a place where the STL would have made this SO much easier ... { ++rv; } return (rv); } bool MW4AI::AnyWeaponReady(MechWarrior4::MWObject& object, Stuff::Time min_delay, Stuff::Time max_delay) { Stuff::ChainIteratorOf i(&(object.weaponChain)); MechWarrior4::Weapon* weapon = 0; while ((weapon = i.ReadAndNext()) != 0) { if (WeaponReadyToFire(*weapon,min_delay,max_delay) == true) { return (true); } } return (false); } bool MW4AI::AnyWeaponNotReady(MechWarrior4::MWObject& object, Stuff::Time min_delay, Stuff::Time max_delay) { return (NumWeaponsReady(object,min_delay,max_delay) < NumWeapons(object)); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // WeaponCanFire(): indicates whether the given weapon can fire // bool MW4AI::WeaponCanFire(MechWarrior4::Weapon& weapon, Stuff::Scalar line_length, Stuff::Time min_delay, Stuff::Time max_delay) { if ((weapon.executionState == 0) || (weapon.executionState->GetState() == MechWarrior4::Weapon::ExecutionStateEngine::DestroyedState)) { return (false); } if (weapon.ReadyToFire() == false) { return (false); } if (WeaponReadyToFire(weapon,min_delay,max_delay) == false) { return (false); } if (line_length != 0) { // MSL 5.03 Min Distance if (line_length > GetMaxWeaponDistance(weapon)) // if ((line_length > GetMaxWeaponDistance(weapon)) || (line_length < GetMinWeaponDistance(weapon)+1)) { return (false); } } if (weapon.IsDerivedFrom(BombastWeapon::DefaultData) == true) { BombastWeapon* bombast = Cast_Object(BombastWeapon*,&weapon); if (bombast->ReadyToFire() == true) { return (true); } if ((bombast->executionState->GetState() == BombastWeapon::ExecutionStateEngine::ChargingState) && (bombast->percentCharged > 0.9f)) { return (true); } return (false); } return (true); } bool MW4AI::GetWeaponsThatCanFire(std::vector& weapons, bool can_spend_ammo, Stuff::Time min_delay, Stuff::Time max_delay) { bool any_can_fire(false); {for (std::vector::iterator i = weapons.begin(); i != weapons.end(); ++i) { if ((*i) == 0) { continue; } if ((*i)->ReadyToFire() == false) { *i = 0; continue; } if (((*i)->executionState == 0) || ((*i)->executionState->GetState() == MechWarrior4::Weapon::ExecutionStateEngine::DestroyedState)) { *i = 0; continue; } if (WeaponReadyToFire(**i,min_delay,max_delay) == false) { *i = 0; continue; } if ((can_spend_ammo == false) && ((*i)->GetAmmoCount() != -1)) { *i = 0; continue; } any_can_fire = true; }} return (any_can_fire); } bool MW4AI::GetWeaponsThatCanFireAt(std::vector& weapons, const Stuff::Point3D& aim) { bool any_can_fire(false); {for (std::vector::iterator i = weapons.begin(); i != weapons.end(); ++i) { if ((*i) != 0) { if (((*i)->sitePointer != 0) && (GetApproximateLength(aim,(Stuff::Point3D)(*i)->sitePointer->GetLocalToWorld()) > GetMaxWeaponDistance(**i))) { *i = 0; continue; } if ((*i)->IsDerivedFrom(BombastWeapon::DefaultData) == true) { BombastWeapon* bombast = Cast_Object(BombastWeapon*,*i); if (bombast->ReadyToFire() == true) { any_can_fire = true; } else { if ((bombast->executionState->GetState() == BombastWeapon::ExecutionStateEngine::ChargingState) && (bombast->percentCharged > 0.9f)) { any_can_fire = true; } else { *i = 0; } } continue; } any_can_fire = true; } }} return (any_can_fire); } bool MW4AI::GetWeaponsThatCanFireFrom(std::vector& weapons, FireSource fire_source) { if (fire_source == FIRE_FROM_ANYWHERE) { return (true); } bool any_can_fire(false); {for (std::vector::iterator i = weapons.begin(); i != weapons.end(); ++i) { if ((*i) != 0) { if (WeaponIsArmWeapon(**i,(fire_source == FIRE_FROM_LEFT_ARM)) == false) { *i = 0; continue; } any_can_fire = true; } }} return (any_can_fire); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // bool MW4AI::MightHitFriendlies(const Stuff::Line3D& line, Stuff::Scalar line_length, MechWarrior4::MWObject& shooter, MechWarrior4::MWObject* ignore) { if (shooter.GetSensor() == 0) { return (false); } const MechWarrior4::Sensor::sensorDataArray& sensed_data = shooter.GetSensor()->RefreshAndGetSensorData(); {for (int i = 0; i < shooter.GetSensor()->numberOfContacts; ++i) { MechWarrior4::SensorData* d = sensed_data[i]; if (d != 0) { Adept::Entity* current = d->object.GetCurrent(); if ((current != 0) && (current != ignore) && (current->objectID != shooter.objectID)) { bool should_avoid_hitting = false; if (current->GetAlignment() == shooter.GetAlignment()) { should_avoid_hitting = true; } else { if (current->IsDerivedFrom(MWObject::DefaultData) == true) { MWObject* mwobject = Cast_Object(MWObject*,current); if (mwobject->GetTargetDesirability() == MWObject::desirability_never) { should_avoid_hitting = true; } } } if (should_avoid_hitting == true) { MWObject* mwobject = Cast_Object(MWObject*,current); Scalar radius = friendly_fire_stretch_distance; CollisionVolume* cv = mwobject->GetSolidVolume(); if (cv != 0) { radius += cv->m_worldSpaceBounds.sphereRadius; } Stuff::Line3D line2(line); if (line_length != line2.m_length) { line2.m_length = line_length; } if (LinePenetrates(line2,mwobject->GetLineOfSight(),radius) == true) { Stuff::LinearMatrix4D los; shooter.GetLineOfSight(los); if (MatrixFacesPoint(los,(Stuff::Point3D)mwobject->GetLineOfSight(),90.0f * Radians_Per_Degree) == true) { return (true); } } } } } }} return (false); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // SortedChainOfComponentsToVector(): converts a component_chain to a valid component_vector // /* void SortedChainOfComponentsToVector(component_chain& chain, component_vector& v) { const int first_component = Adept::DamageObject::LeftLegArmorZone; const int last_component = Adept::DamageObject::HeadArmorZone; {for (int i = first_component; i <= last_component; ++i) { Adept::DamageObject* damage_object = chain.Find(i); if ((damage_object != 0) && (damage_object->parentEntity != 0)) { v.push_back(damage_object); } }} } */ //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // NormalizedDamage(): returns a normalized current damage value for the given damage object, where 1=undamaged and 0=destroyed // inline Stuff::Scalar NormalizedDamage(const Adept::DamageObject& damage_object) { if (damage_object.baseArmorValue == 0) { return (0); } if (damage_object.currentArmorValue >= damage_object.baseArmorValue) { return (1); } return (damage_object.currentArmorValue / damage_object.baseArmorValue); } class DamageObjectEvaluator { public: virtual Stuff::Scalar Evaluate(const Adept::DamageObject& damage_object) const = 0; }; class DamageObjectEvaluator_MostDamaged : public DamageObjectEvaluator { public: Stuff::Scalar Evaluate(const Adept::DamageObject& damage_object) const { return (1 - NormalizedDamage(damage_object)); } }; class DamageObjectEvaluator_LeastDamaged : public DamageObjectEvaluator { public: Stuff::Scalar Evaluate(const Adept::DamageObject& damage_object) const { return (NormalizedDamage(damage_object)); } }; inline Adept::DamageObject* GetBestDamageObject(const component_vector& v, const DamageObjectEvaluator& evaluator) { if (v.size() == 0) { return (0); } component_vector best_fit_list; {for (component_vector::const_iterator i = v.begin(); i != v.end(); ++i) { if (best_fit_list.size() == 0) { best_fit_list.push_back(*i); } else { Stuff::Scalar my_score = evaluator.Evaluate(**i); Stuff::Scalar best_fit_list_score = evaluator.Evaluate(**(best_fit_list.begin())); if (my_score > best_fit_list_score) { best_fit_list.clear(); } if (my_score >= best_fit_list_score) { best_fit_list.push_back(*i); } } }} if (best_fit_list.size() > 0) { return (best_fit_list[gos_rand() % best_fit_list.size()]); } return (0); } Adept::DamageObject* FindComponent(const component_vector& components, int zone) { {for (component_vector::const_iterator i = components.begin(); i != components.end(); ++i) { if ((*i)->armorZone == zone) { return (*i); } }} return (0); } Adept::DamageObject* FindMostArmedComponent(MechWarrior4::MWObject& who, const component_vector& components) { int battle_values[5]; {for (int i = 0; i < 5; ++i) { battle_values[i] = 0; }} { Stuff::ChainIteratorOf i(&(who.weaponChain)); MechWarrior4::Weapon* weapon = 0; while ((weapon = i.ReadAndNext()) != 0) { if ((weapon->GetParentVehicle() != 0) && (weapon->IsDestroyed() == false) && (weapon->GetGameModel() != 0)) { MWInternalDamageObject *zone = weapon->GetParentVehicle()->internalDamageObjects.Find(weapon->internalLocation); if (zone != 0) { int bv = weapon->GetGameModel()->battleValue; switch (zone->damageZone) { case MWInternalDamageObject::LeftArmZone: { battle_values[0] += bv; break; } case MWInternalDamageObject::LeftTorsoZone: { battle_values[1] += bv; break; } case MWInternalDamageObject::CenterTorsoZone: { battle_values[2] += bv; break; } case MWInternalDamageObject::RightTorsoZone: { battle_values[3] += bv; break; } case MWInternalDamageObject::RightArmZone: { battle_values[4] += bv; break; } } } } } } DamageObject* best_object = 0; int iters = 0; while ((best_object == 0) && (iters < 4)) { int best_index = 0; {for (int i = 1; i < 5; ++i) { if (battle_values[i] > battle_values[best_index]) { best_index = i; } }} int best_zone = Adept::DamageObject::CenterTorsoArmorZone; switch (best_index) { case 0: { best_zone = Adept::DamageObject::LeftArmArmorZone; break; } case 1: { best_zone = Adept::DamageObject::LeftTorsoArmorZone; break; } case 3: { best_zone = Adept::DamageObject::RightTorsoArmorZone; break; } case 4: { best_zone = Adept::DamageObject::RightArmArmorZone; break; } } DamageObject* best_object = FindComponent(components,best_zone); if (best_object != 0) { return (best_object); } battle_values[best_index] = -1; ++iters; } return (FindComponent(components,Adept::DamageObject::CenterTorsoArmorZone)); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // GetComponent_MostDamaged(): gets the most damaged component of a given list // Adept::DamageObject* MW4AI::GetComponent_MostDamaged(MechWarrior4::MWObject& who, const component_vector& v) { Verify(v.size() > 0); bool any_damaged(false); {for (component_vector::const_iterator i = v.begin(); i != v.end(); ++i) { if ((*i)->GetCurrentDamageLevel() == Adept::DamageObject::Damaged) { any_damaged = true; } switch ((*i)->armorZone) { case Adept::DamageObject::LeftLegArmorZone: { if ((*i)->GetCurrentDamageLevel() == Adept::DamageObject::Destroyed) { {for (component_vector::const_iterator j = v.begin(); j != v.end(); ++j) { if ((*j)->armorZone == Adept::DamageObject::RightLegArmorZone) { return (*j); } }} } break; } case Adept::DamageObject::RightLegArmorZone: { if ((*i)->GetCurrentDamageLevel() == Adept::DamageObject::Destroyed) { {for (component_vector::const_iterator j = v.begin(); j != v.end(); ++j) { if ((*j)->armorZone == Adept::DamageObject::LeftLegArmorZone) { return (*j); } }} } break; } case Adept::DamageObject::CenterTorsoArmorZone: { if (((*i)->GetCurrentDamageLevel() == Adept::DamageObject::Damaged) || ((*i)->GetCurrentDamageLevel() == Adept::DamageObject::Destroyed)) { if ((*i)->armorLevel < 0.7f) { return (*i); } } break; } } }} if (any_damaged == false) { int best_zone; switch (gos_rand() & 0x07) { case 0: { best_zone = Adept::DamageObject::LeftArmArmorZone; break; } case 1: { best_zone = Adept::DamageObject::LeftTorsoArmorZone; break; } case 2: { best_zone = Adept::DamageObject::RightTorsoArmorZone; break; } case 3: { best_zone = Adept::DamageObject::RightArmArmorZone; break; } default: { best_zone = Adept::DamageObject::CenterTorsoArmorZone; break; } } Adept::DamageObject* damage_object = FindComponent(v,best_zone); if (damage_object != 0) { return (damage_object); } } DamageObjectEvaluator_MostDamaged func; return (GetBestDamageObject(v,func)); } Adept::DamageObject* MW4AI::GetComponent_LeastDamaged(const component_vector& v) { Verify(v.size() > 0); DamageObjectEvaluator_LeastDamaged func; return (GetBestDamageObject(v,func)); } Adept::DamageObject* MW4AI::GetComponent_Random(const component_vector& v) { Verify(v.size() > 0); if (v.size () == 0) { return (0); } return (v[gos_rand() % v.size()]); } Adept::DamageObject* MW4AI::GetComponent_TorsoThenTopDown(const component_vector& v) { Verify(v.size() > 0); Adept::DamageObject* torso = FindComponent(v,Adept::DamageObject::CenterTorsoArmorZone); if (torso != 0) { return (torso); } Stuff::Point3D highest(0,-1000,0); component_vector::const_iterator best = v.end(); {for (component_vector::const_iterator i = v.begin(); i != v.end(); ++i) { if ((*i)->parentEntity != 0) { Stuff::Point3D pos = (Stuff::Point3D)(*i)->parentEntity->GetLocalToWorld(); if (pos.y > highest.y) { highest = pos; best = i; } } }} if (best == v.end()) { return (*v.begin()); } return (*best); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // bool MW4AI::WeaponIsArmWeapon(const MechWarrior4::Weapon& weapon, bool left) { if ((weapon.sitePointer != 0) && (weapon.sitePointer->GetParent() != 0) && (weapon.sitePointer->GetParent()->damageObject != 0) && (weapon.sitePointer->GetParent()->damageObject->internalDamageObject != 0)) { Adept::InternalDamageObject* ido = weapon.sitePointer->GetParent()->damageObject->internalDamageObject; if (left == true) { if (ido->damageZone == Adept::InternalDamageObject::LeftArmZone) { return (true); } } else { if (ido->damageZone == Adept::InternalDamageObject::RightArmZone) { return (true); } } } return (false); } Stuff::LinearMatrix4D MW4AI::WeaponSiteLocalToWorld(const MechWarrior4::Weapon& weapon) { Verify(weapon.sitePointer != 0); Verify(weapon.sitePointer->GetParent() != 0); Verify(weapon.sitePointer->GetParent()->damageObject != 0); Verify(weapon.sitePointer->GetParent()->damageObject->internalDamageObject != 0); Verify(weapon.sitePointer->GetParent()->damageObject->internalDamageObject->parentEntity != 0); return (weapon.sitePointer->GetLocalToWorld()); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // bool MW4AI::HasAnyUnlimitedAmmoWeapons(MechWarrior4::MWObject& object) { Stuff::ChainIteratorOf i(&(object.weaponChain)); MechWarrior4::Weapon* weapon = 0; while ((weapon = i.ReadAndNext()) != 0) { if (weapon->GetAmmoCount() == -1) { return (true); } } return (false); } bool MW4AI::HasAnyProjectileWeapons(MechWarrior4::MWObject& object) { Stuff::ChainIteratorOf i(&(object.weaponChain)); MechWarrior4::Weapon* weapon = 0; while ((weapon = i.ReadAndNext()) != 0) { if (weapon->IsDerivedFrom(MechWarrior4::ProjectileWeapon::DefaultData) == true) { return (true); } } return (false); } Weapon* FindFarthestWeapon(const std::vector& weapons, const Stuff::Point3D& point) { if (weapons.size() == 0) { return (0); } Stuff::Scalar best_distance = 0; std::vector::const_iterator best = weapons.end(); {for (std::vector::const_iterator i = weapons.begin(); i != weapons.end(); ++i) { const Stuff::Scalar distance = GetLengthSquared(point,(Stuff::Point3D)(*i)->sitePointer->GetLocalToWorld()); if (distance >= best_distance) { best_distance = distance; best = i; } }} Verify(best != weapons.end()); return (*best); } void MW4AI::GetLeftmostAndRightmostWeapons(MechWarrior4::MWObject& object, std::vector& weapons) { Stuff::ChainIteratorOf i(&(object.weaponChain)); MechWarrior4::Weapon* weapon = 0; std::vector all_weapons; while ((weapon = i.ReadAndNext()) != 0) { all_weapons.push_back(weapon); } if (all_weapons.size() < 2) { return; } weapons.push_back(FindFarthestWeapon(all_weapons,GetWeaponCenter(object))); weapons.push_back(FindFarthestWeapon(all_weapons,(Stuff::Point3D)weapons.back()->sitePointer->GetLocalToWorld())); } Stuff::Point3D MW4AI::GetWeaponCenter(MechWarrior4::MWObject& object) { Stuff::ChainIteratorOf i(&(object.weaponChain)); MechWarrior4::Weapon* weapon = 0; Stuff::Point3D center(0,0,0); Stuff::Scalar num_counted(0); while ((weapon = i.ReadAndNext()) != 0) { center += (Stuff::Point3D)weapon->sitePointer->GetLocalToWorld(); num_counted += 1; } if (num_counted == 0) { return (Stuff::Point3D(0,0,0)); } center.x /= num_counted; center.y /= num_counted; center.z /= num_counted; return (center); } void MW4AI::DamageObjectChainToVector(Stuff::SortedChainOf& chain, component_vector& v) { AutoHeap local_heap (g_CombatAIHeap); Stuff::SortedChainIteratorOf i(&chain); Adept::DamageObject* damage_object = 0; while ((damage_object = i.ReadAndNext()) != 0) { if ((damage_object->parentEntity != 0) && (damage_object->armorZone != Adept::DamageObject::HeadArmorZone)) { if (damage_object->GetCurrentDamageLevel() == Adept::DamageObject::Destroyed) { switch (damage_object->armorZone) { case Adept::DamageObject::LeftArmArmorZone: { v.push_back(damage_object); break; } case Adept::DamageObject::RightArmArmorZone: case Adept::DamageObject::RightTorsoArmorZone: case Adept::DamageObject::LeftTorsoArmorZone: case Adept::DamageObject::CenterTorsoArmorZone: case Adept::DamageObject::CenterRearTorsoArmorZone: { v.push_back(damage_object); break; } } } else { v.push_back(damage_object); } } } } void MW4AI::SetRaySourceToBestComponent(Adept::Entity__CollisionQuery& query) { if ((query.m_raySource != 0) && (query.m_raySource != Adept::Map::GetInstance()) && (query.m_line != 0) && (query.m_raySource->IsDerivedFrom(MWObject::DefaultData) == true)) { MWObject* mwobject = Cast_Object(MWObject*,query.m_raySource); component_vector components; DamageObjectChainToVector(mwobject->damageObjects,components); Adept::Entity* nearest = 0; Stuff::Scalar least_distance = 100000.0f; least_distance *= least_distance; {for (component_vector::iterator i = components.begin(); i != components.end(); ++i) { if ((*i)->parentEntity != 0) { Stuff::Point3D damage_object_point((*i)->parentEntity->GetLocalToWorld()); Stuff::Point3D closest_point; query.m_line->GetClosestPointTo(damage_object_point,&closest_point); Stuff::Scalar distance(GetLengthSquared(damage_object_point,closest_point)); if (distance < least_distance) { nearest = (*i)->parentEntity; least_distance = distance; } } }} if (nearest != 0) { query.m_raySource = nearest; } } }