#include "MW4Headers.hpp" #include "AI_FireStyle.hpp" #include "Weapon.hpp" #include "AI_Weapons.hpp" #include "aiutils.hpp" #include "MWObject.hpp" #include "AI_FireData.hpp" #include "ProjectileWeapon.hpp" #include "HighExplosiveWeapon.hpp" #include "LongTomWeapon.hpp" #include "ArtilleryWeapon.hpp" #include "NetWeapon.hpp" // MSL 5.03 RTX //#include "rtxweaponsub.hpp" #include #include #include "NarcWeaponSubsystem.hpp" #include "SubsystemClassData.hpp" #include "MWDamageObject.hpp" #include "ai.hpp" #pragma warning(push) #include #pragma warning(pop) const double linked_fire_shot_delay = 0.3f; const Stuff::Scalar min_high_explosive_distance = 80.0f; extern __int64 tCombatAITime_WeaponFiring; extern __int64 tWeaponFiring; using namespace MW4AI; bool FireStyles::FireStyle::gHeatManagementEnabled = false; // TODO: remove this eventually ... //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // General fire styles functions // Auto_Ptr FireStyles::CreateFireStyle(FireStyleID id) { switch (id) { case FireStyles::FIRE_MAXIMUM: { return (new FireStyles::MaximumFire()); } case FireStyles::FIRE_OPPORTUNITY: { return (new FireStyles::OpportunityFire()); } } Verify(!"Should never get here."); return (new FireStyles::MaximumFire()); } void FireStyles::VerifyFireStyle(FireStyleID id) { Verify(id >= FireStyles::FIRE_FIRST); Verify(id <= FireStyles::FIRE_LAST); } std::string FireStyles::FireStyleToString(FireStyleID id) { VerifyFireStyle(id); switch (id) { case FireStyles::FIRE_MAXIMUM: { return ("Maximum"); } case FireStyles::FIRE_OPPORTUNITY: { return ("Opportunity"); } default: { Verify("Should not get here" == 0); } } return (""); } MechWarrior4::MWObject* SelfOrParentVehicle(Adept::Entity* entity) { if ((entity == 0) || (entity->IsDerivedFrom(MechWarrior4::MWObject::DefaultData) == false)) { return (0); } MechWarrior4::MWObject* mwobject = Cast_Object(MechWarrior4::MWObject*,entity); Check_Object(mwobject); if (mwobject->GetParentVehicle() == 0) { return (mwobject); } return (mwobject->GetParentVehicle()); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Functors: these plug in to TrueForAllWeapons(), TrueForAnyWeapon(), etc. to perform the appropriate task or query // #define FUNCTOR_HEADER(classname) \ public: \ classname(const FireParamPackage& params) \ : m_Params(params) \ { \ } \ private: \ const FireParamPackage& m_Params; #define REMOVE true #define NO_REMOVE false class Fire_Functor { public: virtual bool Execute(MechWarrior4::Weapon& weapon, MechWarrior4::WeaponUpdate& weapon_update) = 0; }; class Functor_CanFire : public Fire_Functor { FUNCTOR_HEADER(Functor_CanFire); public: bool Execute(MechWarrior4::Weapon& weapon, MechWarrior4::WeaponUpdate& weapon_update) { Check_Object(&weapon); if ((m_Params.can_spend_ammo == false) && (weapon.GetAmmoCount() != -1)) { return (REMOVE); } if (WeaponCanFire(weapon,m_Params.fire_data.GetLine().m_length,m_Params.min_delay,m_Params.max_delay) == false) { return (REMOVE); } return (NO_REMOVE); } }; class Functor_CorrectFireSource : public Fire_Functor { FUNCTOR_HEADER(Functor_CorrectFireSource); public: bool Execute(MechWarrior4::Weapon& weapon, MechWarrior4::WeaponUpdate& weapon_update) { Check_Object(&weapon); switch (m_Params.where_to_fire_from) { case FIRE_FROM_LEFT_ARM: { if (WeaponIsArmWeapon(weapon,true) == false) { return (REMOVE); } break; } case FIRE_FROM_RIGHT_ARM: { if (WeaponIsArmWeapon(weapon,false) == false) { return (REMOVE); } break; } } return (NO_REMOVE); } }; class Functor_DoesDamage : public Fire_Functor { FUNCTOR_HEADER(Functor_DoesDamage); public: bool Execute(MechWarrior4::Weapon& weapon, MechWarrior4::WeaponUpdate& weapon_update) { Check_Object(&weapon); if (weapon.IsDerivedFrom(MechWarrior4::ProjectileWeapon::DefaultData) == true) { MechWarrior4::ProjectileWeapon* projectile_weapon = Cast_Object(MechWarrior4::ProjectileWeapon*,&weapon); const ProjectileWeapon__GameModel* game_model = projectile_weapon->GetGameModel(); if (game_model->damageAmount <= 0) { return (REMOVE); } if (weapon.IsDerivedFrom(MechWarrior4::HighExplosiveWeapon::DefaultData) == true) { if (m_Params.can_commit_suicide == false) { return (REMOVE); } if (m_Params.fire_data.GetLine().m_length > min_high_explosive_distance) { return (REMOVE); } return (NO_REMOVE); } if (weapon.IsDerivedFrom(MechWarrior4::ArtilleryWeapon::DefaultData) == true) { return (REMOVE); } if ((m_Params.can_fire_narc == false) && (weapon.IsDerivedFrom(MechWarrior4::NarcWeapon::DefaultData) == true)) { return (REMOVE); } } return (NO_REMOVE); } }; class Functor_Fire : public Fire_Functor { FUNCTOR_HEADER(Functor_Fire); public: bool Execute(MechWarrior4::Weapon& weapon, MechWarrior4::WeaponUpdate& weapon_update) { Check_Object(&weapon); Verify(WeaponCanFire(weapon,0,m_Params.min_delay,m_Params.max_delay) == true); std::pair result = CalculateFireData(m_Params,weapon); if ((m_Params.per_weapon_raycasting == true) && (weapon.IsDerivedFrom(MechWarrior4::MissileWeapon::DefaultData) == false)) { FireData ray_cast_data(m_Params.fire_data.GetSource(), weapon, result.first.GetDest()); ray_cast_data.Project(); Adept::Entity* non_const_who_to_hit = const_cast(m_Params.who_to_hit); switch (ray_cast_data.GetHitResult(non_const_who_to_hit)) { case FireData::HIT_NOTHING: case FireData::HIT_SOMETHING_ELSE: { return (REMOVE); } } } { COMBAT_LOGIC("Update Attacking::Firing::With Current Query::Weapon"); TIME_FUNCTION(tWeaponFiring); TIME_FUNCTION(tCombatAITime_WeaponFiring); result.first.GetQuery().m_raySource = m_Params.component; weapon.FireWeapon(&(result.first.GetQuery()),result.second, Stuff::Point3D::Identity); } // begin: fill in all the weapon_update members weapon_update.targetOffset = Point3D::Identity; if ((weapon_update.target.GetCurrent() != 0) && (weapon_update.target.GetCurrent() != Adept::Map::GetInstance())) { weapon_update.targetOffset.MultiplyByInverse(m_Params.fire_data.GetDest(),weapon_update.target.GetCurrent()->GetLocalToWorld()); } else { weapon_update.targetOffset = m_Params.fire_data.GetDest(); } weapon_update.lockTime = (Stuff::Scalar)result.second; weapon_update.weaponFired |= (0x1 << weapon.GetWeaponID()); if (weapon.IsDerivedFrom(MechWarrior4::MissileWeapon::DefaultData) == true) { MechWarrior4::MissileWeapon* missile_weapon = Cast_Object(MechWarrior4::MissileWeapon*,&weapon); weapon_update.AntiWeaponCount[weapon.GetWeaponID()] = missile_weapon->GetAMSNumber(); } // end return (NO_REMOVE); } }; class Functor_HeatLevelOK : public Fire_Functor { public: Functor_HeatLevelOK(const FireParamPackage& params) : m_Params(params) , m_HeatGeneratedSoFar(0) { } private: const FireParamPackage& m_Params; Stuff::Scalar m_HeatGeneratedSoFar; public: bool Execute(MechWarrior4::Weapon& weapon, MechWarrior4::WeaponUpdate& weapon_update) { Check_Object(&weapon); Stuff::Scalar heat = weapon.GetTotalHeatGenerated(); if ((m_Params.can_generate_heat == false) && (heat > 0)) { return (REMOVE); } if (FireStyles::FireStyle::gHeatManagementEnabled == false) { return (NO_REMOVE); } if (weapon.IsDerivedFrom(MechWarrior4::HighExplosiveWeapon::DefaultData) == true) { return (NO_REMOVE); } if (m_Params.current_heat + m_HeatGeneratedSoFar + heat >= (m_Params.max_heat * 0.9f)) { return (REMOVE); } m_HeatGeneratedSoFar += heat; return (NO_REMOVE); } }; class Functor_HitsEntity : public Fire_Functor { public: Functor_HitsEntity(const FireParamPackage& params, Adept::Entity* entity) : m_Params(params) , m_Entity(entity) { } private: const FireParamPackage& m_Params; Adept::Entity* m_Entity; public: bool Execute(MechWarrior4::Weapon& weapon, MechWarrior4::WeaponUpdate& weapon_update) { Verify(WeaponCanFire(weapon,0,m_Params.min_delay,m_Params.max_delay) == true); if (weapon.IsDerivedFrom(MechWarrior4::HighExplosiveWeapon::DefaultData) == true) { return (NO_REMOVE); } if ((weapon.GetGameModel() != 0) && (weapon.GetGameModel()->splashRadius > m_Params.fire_data.GetLine().m_length)) { return (REMOVE); } return (NO_REMOVE); } }; class Functor_WontFriendlyFire : public Fire_Functor { public: Functor_WontFriendlyFire(const FireParamPackage& params) : m_Params(params) { } private: const FireParamPackage& m_Params; public: bool Execute(MechWarrior4::Weapon& weapon, MechWarrior4::WeaponUpdate& weapon_update) { if (m_Params.fire_data.GetSource().IsDerivedFrom(MWObject::DefaultData) == true) { MWObject* mwobject = Cast_Object(MWObject*,&m_Params.fire_data.GetSource()); if (MightHitFriendlies(m_Params.fire_data.GetLine(),GetMaxWeaponDistance(weapon),*mwobject,m_Params.intended_target) == true) { return (REMOVE); } } return (NO_REMOVE); } }; #undef FUNCTOR_EXECUTE_FUNCTION typedef std::vector weapon_list; typedef std::vector functor_list; void GetQualifiedWeapons(Stuff::ChainOf& weapons, functor_list& qualifiers, weapon_list& weapons_out, MechWarrior4::WeaponUpdate& weapon_update) { Stuff::ChainIteratorOf i(&weapons); MechWarrior4::Weapon* weapon = 0; while ((weapon = i.ReadAndNext()) != 0) { Check_Object(weapon); if (weapon->IsDestroyed() == false) { bool qualified = true; {for (functor_list::iterator i = qualifiers.begin(); i != qualifiers.end(); ++i) { if ((*i)->Execute(*weapon,weapon_update) == REMOVE) { qualified = false; break; } }} if (qualified == true) { weapons_out.push_back(weapon); } } } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Maximum fire : fire as much as you can, as often as you can // void FireStyles::MaximumFire::Execute(const FireParamPackage& params, Stuff::ChainOf& weapons, Adept::Entity* target, MechWarrior4::WeaponUpdate& weapon_update) { Functor_CanFire can_fire(params); // which weapons can hit the target point? Functor_CorrectFireSource correct_fire_source(params); // which weapons fire from the correct source (left arm only/right arm only/any & all)? Functor_DoesDamage does_damage(params); // which weapons actually do some kind of damage? Functor_HeatLevelOK heat_level_ok(params); // which weapons can fire without overheating? Functor_HitsEntity hits_entity(params,target); // which weapons hit the intended entity? Functor_WontFriendlyFire no_friendly_fire(params); // which weapons have no chance of causing friendly fire Functor_Fire fire(params); // OK, fire! functor_list qualifiers; qualifiers.push_back(&can_fire); qualifiers.push_back(&correct_fire_source); qualifiers.push_back(&does_damage); qualifiers.push_back(&heat_level_ok); qualifiers.push_back(&hits_entity); qualifiers.push_back(&no_friendly_fire); qualifiers.push_back(&fire); weapon_list w; GetQualifiedWeapons(weapons,qualifiers,w,weapon_update); }