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

490 lines
12 KiB
C++

#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 <Adept\Map.hpp>
#include <Adept\Site.hpp>
#include "NarcWeaponSubsystem.hpp"
#include "SubsystemClassData.hpp"
#include "MWDamageObject.hpp"
#include "ai.hpp"
#pragma warning(push)
#include <stlport\algorithm>
#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::FireStyle> 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<FireData,Stuff::Time> 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<Adept::Entity*>(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<MechWarrior4::Weapon*> weapon_list;
typedef std::vector<Fire_Functor*> functor_list;
void GetQualifiedWeapons(Stuff::ChainOf<MechWarrior4::Weapon*>& weapons,
functor_list& qualifiers,
weapon_list& weapons_out,
MechWarrior4::WeaponUpdate& weapon_update)
{
Stuff::ChainIteratorOf<MechWarrior4::Weapon*> 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<MechWarrior4::Weapon *>& 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);
}