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.
1089 lines
26 KiB
C++
1089 lines
26 KiB
C++
#include "MW4Headers.hpp"
|
|
|
|
#include "AI_Weapons.hpp"
|
|
|
|
#include "BeamWeapon.hpp"
|
|
#include "ProjectileWeapon.hpp"
|
|
#include "MWObject.hpp"
|
|
#include "MWDamageObject.hpp"
|
|
#include <Adept\CollisionGrid.hpp>
|
|
#include "aiutils.hpp"
|
|
#include "bombastweapon.hpp"
|
|
#include <Adept\Site.hpp>
|
|
#include "SubsystemClassData.hpp"
|
|
#include <Adept\Map.hpp>
|
|
#include "Sensor.hpp"
|
|
#include <Adept\CollisionVolume.hpp>
|
|
|
|
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<MechWarrior4::Weapon*> 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<MechWarrior4::Weapon*> 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<MechWarrior4::Weapon*> 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<MechWarrior4::Weapon*> 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<MechWarrior4::Weapon*> 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<MechWarrior4::Weapon*> 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<Weapon *>& weapons,
|
|
bool can_spend_ammo,
|
|
Stuff::Time min_delay,
|
|
Stuff::Time max_delay)
|
|
{
|
|
bool any_can_fire(false);
|
|
{for (std::vector<Weapon*>::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<Weapon *>& weapons,
|
|
const Stuff::Point3D& aim)
|
|
{
|
|
bool any_can_fire(false);
|
|
|
|
{for (std::vector<Weapon*>::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<Weapon *>& weapons,
|
|
FireSource fire_source)
|
|
{
|
|
if (fire_source == FIRE_FROM_ANYWHERE)
|
|
{
|
|
return (true);
|
|
}
|
|
|
|
bool any_can_fire(false);
|
|
{for (std::vector<Weapon*>::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<MechWarrior4::Weapon*> 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<MechWarrior4::Weapon*> 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<MechWarrior4::Weapon*> 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<MechWarrior4::Weapon*>& weapons, const Stuff::Point3D& point)
|
|
{
|
|
if (weapons.size() == 0)
|
|
{
|
|
return (0);
|
|
}
|
|
|
|
Stuff::Scalar best_distance = 0;
|
|
std::vector<MechWarrior4::Weapon*>::const_iterator best = weapons.end();
|
|
|
|
{for (std::vector<MechWarrior4::Weapon*>::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<MechWarrior4::Weapon*>& weapons)
|
|
{
|
|
Stuff::ChainIteratorOf<MechWarrior4::Weapon*> i(&(object.weaponChain));
|
|
MechWarrior4::Weapon* weapon = 0;
|
|
std::vector<MechWarrior4::Weapon*> 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<MechWarrior4::Weapon*> 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<Adept::DamageObject*,int>& chain, component_vector& v)
|
|
{
|
|
AutoHeap local_heap (g_CombatAIHeap);
|
|
Stuff::SortedChainIteratorOf<Adept::DamageObject*,int> 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;
|
|
}
|
|
}
|
|
}
|
|
|