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

1013 lines
24 KiB
C++

#include "MW4Headers.hpp"
#include "AI_SituationalAnalysis.hpp"
#include "Vehicle.hpp"
#include "Sensor.hpp"
#include "Torso.hpp"
#include "MWMission.hpp"
#include "rail_move.hpp"
#include "Mech.hpp"
#include "AI_Action.hpp"
#include "Airplane.hpp"
const unsigned int max_enemies_list_size = 7;
const Stuff::Scalar prediction_time = 2.0f;
const Stuff::Scalar max_height_differential = 0.45f;
extern __int64 tCombatAITime_SituationalAnalysis;
extern __int64 tCombatAITime_SituationalAnalysis_Generate;
extern __int64 tCombatAITime_SituationalAnalysis_Evaluate;
extern __int64 tCombatAITime_SituationalAnalysis_PointRemoval;
using namespace MW4AI;
using namespace MW4AI::SituationalAnalysis;
bool MW4AI::SituationalAnalysis::PointEvaluator::m_PostEvaluationState = false;
bool PointIsOutsideMissionBounds(const Stuff::Point3D& point)
{
Verify(Adept::Mission::GetInstance() != 0);
Verify(Adept::Mission::GetInstance()->IsDerivedFrom(MechWarrior4::MWMission::DefaultData) == true);
MechWarrior4::MWMission* mission = Cast_Object(MechWarrior4::MWMission*,Adept::Mission::GetInstance());
if (mission->GetMissionArea(point) != MWMission::InMissionArea)
{
return (true);
}
return (false);
}
inline void GetEnemiesList(MechWarrior4::MWObject& self, enemies_list& enemies)
{
Verify(&self != 0);
if (self.GetSensor() == 0)
{
return;
}
const MechWarrior4::Sensor::sensorDataArray& sensed_data = self.GetSensor()->RefreshAndGetSensorData();
{for (int i = 0;
i < self.GetSensor()->numberOfContacts;
++i)
{
MechWarrior4::SensorData* d = sensed_data[i];
if ((d != 0) &&
(d->object.GetCurrent() != 0) &&
(d->object.GetCurrent()->objectID != self.objectID) &&
(d->object.GetCurrent()->GetAlignment() != self.GetAlignment()) &&
(d->object.GetCurrent()->IsDerivedFrom(MWObject::DefaultData)))
{
enemies.push_back(d->object.GetCurrent());
}
}}
}
inline void LimitEnemiesListSize(enemies_list& enemies, enemies_list::difference_type max_size)
{
while (enemies.size() > max_size)
{
enemies_list::iterator element_to_erase = enemies.begin();
element_to_erase += gos_rand() % enemies.size();
enemies.erase(element_to_erase);
}
}
inline bool PointIsValid(TacticInterface& iface, const Stuff::Point3D& my_pos, const Stuff::Point3D& point, bool ignore_mission_bounds)
{
if ((ignore_mission_bounds == false) &&
(iface.PointIsInBadNeighborhood(point) == true))
{
return (false);
}
return (iface.PointIsValid(point,ignore_mission_bounds));
}
inline bool PointShouldBeRemoved(TacticInterface& iface, const Stuff::Point3D& point, Stuff::Scalar minimum_distance, bool ignore_mission_bounds = false)
{
const Stuff::Point3D my_pos(iface.GetSelf().GetLocalToWorld());
minimum_distance *= minimum_distance;
if ((minimum_distance != 0) &&
(GetLengthSquared(point,my_pos) < minimum_distance))
{
return (true);
}
if (PointIsValid(iface,my_pos,point,ignore_mission_bounds) == false)
{
return (true);
}
return (false);
}
inline bool ShouldConsiderWater(TacticInterface& iface)
{
if (UserConstants::Instance() == 0)
{
return (false);
}
if ((iface.GetEliteLevel() >= UserConstants::Instance()->Get(UserConstants::min_prefer_water)) &&
(iface.GetEliteLevel() <= UserConstants::Instance()->Get(UserConstants::max_prefer_water)) &&
(iface.GetSelf().IsDerivedFrom(MechWarrior4::Mech::DefaultData) == true))
{
return (true);
}
return (false);
}
inline Fuzzy MW4AI::SituationalAnalysis::GetWaterValue(const Stuff::Point3D& point)
{
if (g_MissionGraph != 0)
{
if ((point.x <= MW4AI::MinX) ||
(point.x >= MW4AI::MaxX) ||
(point.z <= MW4AI::MinZ) ||
(point.z >= MW4AI::MaxZ))
{
return (0.5f);
}
unsigned short passability = g_MissionGraph->Passable(point.x,point.z);
int water = (passability & WATER_MASK) >> WATER_SHIFT;
switch (water)
{
case NO_WATER:
{
return (0.5f);
}
case SHALLOW_WATER:
{
return (0.8f);
}
case MEDIUM_WATER:
{
return (1.0f);
}
case DEEP_WATER:
{
return (0);
}
}
}
return (1);
}
inline Fuzzy MW4AI::SituationalAnalysis::EvaluatePoint(TacticInterface& iface,
const Stuff::Point3D& point,
const PointEvaluator& evaluator,
const enemies_list* enemies)
{
COMBAT_LOGIC("Update Attacking::Situation Analysis::Evaluate");
TIME_FUNCTION(tCombatAITime_SituationalAnalysis_Evaluate);
bool consider_water(ShouldConsiderWater(iface));
Fuzzy rv;
if (enemies != 0)
{
rv = evaluator.Evaluate(iface,*enemies,point);
}
else
{
enemies_list found_enemies;
GetEnemiesList(iface.GetSelf(),found_enemies);
LimitEnemiesListSize(found_enemies,max_enemies_list_size);
rv = evaluator.Evaluate(iface,found_enemies,point);
}
if (consider_water == true)
{
rv *= GetWaterValue(point);
}
Stuff::Point3D target_pos(iface.GetTarget().GetLocalToWorld());
if ((Stuff::Fabs(point.y - target_pos.y) > 8.0f) && // fudge factor to decide when height might matter
(iface.GetSelf().GetTorso() != 0) &&
(iface.GetSelf().GetTorso()->GetGameModel() != 0))
{
const MechWarrior4::Torso::GameModel* model = iface.GetSelf().GetTorso()->GetGameModel();
Stuff::Scalar pitch_angle(iface.GetMaxFireCheatAngle() + model->pitchRadius);
if (MatrixFacesPoint_PitchOnly(iface.GetTarget().GetLocalToWorld(),point,pitch_angle) == false)
{
return (0);
}
}
Stuff::Point3D my_pos(iface.GetSelf().GetLocalToWorld());
Stuff::Scalar height_differential(Stuff::Fabs(my_pos.y - point.y));
my_pos.y = point.y;
Stuff::Scalar distance_to_point(GetApproximateLength(my_pos,point));
if (height_differential > distance_to_point * max_height_differential)
{
return (rv * 0.1f);
}
const Stuff::Scalar hide_direction = iface.GetCurrentVulnerableComponent();
if (hide_direction != 0)
{
// TODO: should actually evaluate for all enemies -- also, target may be friendly (defend tactic)!!!
const Stuff::Scalar yaw_to_point = YawToPoint(iface.GetSelf().GetLocalToWorld(),point);
const Stuff::Scalar yaw_to_target = YawToPoint(iface.GetSelf().GetLocalToWorld(),target_pos);
Stuff::Scalar diff = yaw_to_point - yaw_to_target;
while (diff > (Stuff::Pi * 2))
{
diff -= (Stuff::Pi * 2);
}
while (diff < -(Stuff::Pi * 2))
{
diff += (Stuff::Pi * 2);
}
if (Actions::ShouldMoveForwardToPoint(iface,point) == true)
{
if (Sign(diff) == Sign(hide_direction))
{
rv *= 1.5f;
}
else
{
rv *= 0.75f;
}
}
else
{
if (Sign(diff) != Sign(hide_direction))
{
rv *= 1.5f;
}
else
{
rv *= 0.75f;
}
}
}
Clamp(rv,0,1);
return (rv);
}
inline void RankPoints(TacticInterface& iface,
const PointEvaluator& evaluator,
Stuff::Scalar minimum_distance,
const point_list& points,
ranked_point_list& map_of_points)
{
enemies_list enemies;
GetEnemiesList(iface.GetSelf(),enemies);
LimitEnemiesListSize(enemies,max_enemies_list_size);
{for (point_list::const_iterator i = points.begin();
i != points.end();
++i)
{
ranked_point p(EvaluatePoint(iface,*i,evaluator,&enemies),*i);
map_of_points.insert(p);
}}
}
std::pair<bool,Stuff::Point3D> TryGenerator(TacticInterface& iface,
const RegionGenerator& search_region_generator,
const PointEvaluator& evaluator,
Stuff::Scalar minimum_distance,
int generation_iteration,
bool ignore_mission_bounds)
{
point_list points;
{
COMBAT_LOGIC("Update Attacking::Situation Analysis::Generate");
TIME_FUNCTION(tCombatAITime_SituationalAnalysis_Generate);
search_region_generator.Generate(iface,points,generation_iteration);
}
ranked_point_list map_of_points;
RankPoints(iface,evaluator,minimum_distance,points,map_of_points);
{
COMBAT_LOGIC("Update Attacking::Situation Analysis::Point Removal");
TIME_FUNCTION(tCombatAITime_SituationalAnalysis_PointRemoval);
{for (ranked_point_list::reverse_iterator i = map_of_points.rbegin();
i != map_of_points.rend();
++i)
{
if (PointShouldBeRemoved(iface,(*i).second,minimum_distance,ignore_mission_bounds) == false)
{
return (std::pair<bool,Stuff::Point3D>(true,(*i).second));
}
}}
}
return (std::pair<bool,Stuff::Point3D>(false,Stuff::Point3D::Identity));
}
Stuff::Point3D MW4AI::SituationalAnalysis::Evaluate(TacticInterface& iface,
const RegionGenerator& search_region_generator,
const PointEvaluator& evaluator,
Stuff::Scalar minimum_distance)
{
COMBAT_LOGIC("Update Attacking::Situation Analysis");
TIME_FUNCTION(tCombatAITime_SituationalAnalysis);
{for (int i = 0;
i < search_region_generator.Iterations();
++i)
{
std::pair<bool,Stuff::Point3D> result(TryGenerator(iface,
search_region_generator,
evaluator,
minimum_distance,
i,
search_region_generator.IgnoreMissionBounds(iface)));
if (result.first == true)
{
return (result.second);
}
}}
Evaluator_DistanceFrom special_evaluator(FROM_TARGET,20,3000,NEAREST);
Stuff::Point3D point;
if (iface.GetEscapeRegionFocusIfAvailable(point) == true)
{
Generator_Circle circle(point,125);
std::pair<bool,Stuff::Point3D> result(TryGenerator(iface,
circle,
special_evaluator,
minimum_distance,
0,
false));
if (result.first == true)
{
return (result.second);
}
}
Generator_EscapeRegion escape_region;
std::pair<bool,Stuff::Point3D> result(TryGenerator(iface,
escape_region,
special_evaluator,
minimum_distance,
0,
true));
if (result.first == true)
{
return (result.second);
}
return ((Stuff::Point3D)iface.GetSelf().GetLocalToWorld());
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// helper functions
//
inline Stuff::Scalar GetNormalizedBoundedDistance(Stuff::Scalar value, Stuff::Scalar min, Stuff::Scalar max)
{
if (value < min)
{
return (0);
}
if (value > max)
{
return (1);
}
return ((value - min) / (max - min));
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Evaluator_DistanceFrom
//
void MW4AI::SituationalAnalysis::PointEvaluator::SetEvaluationState(bool post_evaluation)
{
m_PostEvaluationState = post_evaluation;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Evaluator_DistanceFrom
//
Evaluator_DistanceFrom::Evaluator_DistanceFrom(FromWho who,
Stuff::Scalar min,
Stuff::Scalar max,
DistanceQualifier want_nearest)
: m_Min(min)
, m_Max(max)
, m_Who(who)
, m_WantNearest(want_nearest)
{
Verify(m_Min < m_Max);
Verify((m_WantNearest == NEAREST) || (m_WantNearest == FARTHEST));
}
Fuzzy Evaluator_DistanceFrom::Evaluate(TacticInterface& i,
const enemies_list& enemies,
const Stuff::Point3D& point) const
{
Stuff::Point3D src;
Fuzzy rv = 0;
if (m_Who == FROM_ALL_ENEMIES)
{
rv = 1;
if (enemies.size() > 0)
{
{for (enemies_list::const_iterator i = enemies.begin();
i != enemies.end();
++i)
{
Stuff::Point3D future_pos;
future_pos = (*i)->EstimateFuturePosition(&future_pos,prediction_time,false);
Fuzzy d = GetNormalizedBoundedDistance(GetApproximateLength(future_pos,point),m_Min,m_Max);
if (d < rv)
{
rv = d;
}
}}
}
}
else
{
Stuff::Point3D other_point;
switch (m_Who)
{
case FROM_SELF:
{
if (GetEvaluationState() == true)
{
other_point = (Stuff::Point3D)i.GetLastMoveOrderPosition();
}
else
{
other_point = (Stuff::Point3D)i.GetSelf().GetLocalToWorld();
}
break;
}
case FROM_TARGET:
{
other_point = (Stuff::Point3D)i.GetTarget().EstimateFuturePosition(&other_point,prediction_time,false);
break;
}
case FROM_NEAREST_ENEMY:
{
if (i.GetNearest(true) == 0)
{
return (0.5f); // TODO: what to return when there is no nearest?
}
other_point = (Stuff::Point3D)i.GetNearest()->EstimateFuturePosition(&other_point,prediction_time,false);
break;
};
default:
{
Verify("Not supported." == 0);
other_point = (Stuff::Point3D)i.GetSelf().GetLocalToWorld();
break;
}
}
rv = GetNormalizedBoundedDistance(GetApproximateLength(other_point,point),m_Min,m_Max);
}
Verify(rv >= 0);
Verify(rv <= 1);
if (m_WantNearest == NEAREST)
{
return (1 - rv);
}
return (rv);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Evaluator_WeightedAverage
//
Evaluator_WeightedAverage::Evaluator_WeightedAverage(Stuff::Scalar weight1, PointEvaluator& evaluator1,
Stuff::Scalar weight2, PointEvaluator& evaluator2)
{
m_Weights.push_back(weight1);
m_Evaluators.push_back(&evaluator1);
m_Weights.push_back(weight2);
m_Evaluators.push_back(&evaluator2);
}
Evaluator_WeightedAverage::Evaluator_WeightedAverage(Stuff::Scalar weight1, PointEvaluator& evaluator1,
Stuff::Scalar weight2, PointEvaluator& evaluator2,
Stuff::Scalar weight3, PointEvaluator& evaluator3)
{
m_Weights.push_back(weight1);
m_Evaluators.push_back(&evaluator1);
m_Weights.push_back(weight2);
m_Evaluators.push_back(&evaluator2);
m_Weights.push_back(weight3);
m_Evaluators.push_back(&evaluator3);
}
Fuzzy Evaluator_WeightedAverage::Evaluate(TacticInterface& i,
const enemies_list& enemies,
const Stuff::Point3D& point) const
{
Verify(m_Evaluators.size() > 0);
Verify(m_Weights.size() == m_Evaluators.size());
Fuzzy running_total = 0;
Stuff::Scalar sum_of_weights = 0;
{for (int index = 0;
index < m_Evaluators.size();
++index)
{
Verify(m_Weights[index] > 0);
running_total += (m_Evaluators[index]->Evaluate(i,enemies,point) * m_Weights[index]);
sum_of_weights += m_Weights[index];
}}
return (running_total / sum_of_weights);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Evaluator_InFrontOf -- used as a last resort when we can't find a decent spot to move to
//
Fuzzy Evaluator_Random::Evaluate(TacticInterface& i,
const enemies_list& enemies,
const Stuff::Point3D& point) const
{
return (Stuff::Random::GetFraction());
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Evaluator_InFrontOf -- used as a last resort when we can't find a decent spot to move to
//
Evaluator_InFrontOf::Evaluator_InFrontOf(FromWho who,
bool in_front,
bool use_torso)
: m_Who(who)
, m_InFront(in_front)
, m_UseTorso(use_torso)
{
Verify(m_Who != FROM_ALL_ENEMIES);
Verify(m_Who != FROM_NEAREST_ENEMY);
}
Fuzzy Evaluator_InFrontOf::Evaluate(TacticInterface& i,
const enemies_list& enemies,
const Stuff::Point3D& point) const
{
Stuff::LinearMatrix4D relative_to;
if (m_Who == FROM_SELF)
{
if (GetEvaluationState() == true)
{
relative_to = i.GetLastMoveOrderPosition();
}
else
{
if ((m_UseTorso == true) &&
(i.GetSelf().GetTorso() != 0) &&
(i.GetSelf().GetTorso()->twistJoint != 0))
{
Check_Object(i.GetSelf().GetTorso());
relative_to = i.GetSelf().GetTorso()->twistJoint->GetLocalToWorld();
}
else
{
relative_to = i.GetSelf().GetLocalToWorld();
}
}
}
else
{
if ((m_UseTorso == true) &&
(i.GetTarget().GetTorso() != 0) &&
(i.GetTarget().GetTorso()->twistJoint != 0))
{
Check_Object(i.GetTarget().GetTorso());
relative_to = i.GetTarget().GetTorso()->twistJoint->GetLocalToWorld();
}
else
{
relative_to = i.GetTarget().GetLocalToWorld();
}
Stuff::Point3D future_pos;
i.GetTarget().EstimateFuturePosition(&future_pos,prediction_time,false);
relative_to.BuildTranslation(future_pos);
}
Stuff::Point3D delta;
delta.Subtract(point,(Stuff::Point3D)relative_to);
if (delta.GetLengthSquared() == 0)
{
return (1);
}
Stuff::UnitVector3D delta_normalized(delta);
Stuff::UnitVector3D local_forward;
relative_to.GetLocalForwardInWorld(&local_forward);
Stuff::Scalar bearing = delta_normalized * local_forward;
bearing = (bearing + 1) * 0.5f;
if (m_InFront == false)
{
bearing = 1 - bearing;
}
if (bearing > 1)
{
return (1);
}
if (bearing < 0)
{
return (0);
}
return (bearing);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Evaluator_45DegreesInFront -- used as a last resort when we can't find a decent spot to move to
//
Evaluator_45DegreesInFront::Evaluator_45DegreesInFront(bool random)
: m_Random(random)
{
}
Fuzzy Evaluator_45DegreesInFront::Evaluate(TacticInterface& i,
const enemies_list& enemies,
const Stuff::Point3D& point) const
{
Stuff::LinearMatrix4D relative_to;
if (GetEvaluationState() == true)
{
relative_to = i.GetLastMoveOrderPosition();
}
else
{
relative_to = i.GetSelf().GetLocalToWorld();
}
Stuff::Point3D delta;
delta.Subtract(point,(Stuff::Point3D)relative_to);
if (delta.GetLengthSquared() == 0)
{
return (1);
}
Stuff::UnitVector3D delta_normalized(delta);
Stuff::UnitVector3D local_forward;
relative_to.GetLocalForwardInWorld(&local_forward);
Stuff::Scalar bearing = delta_normalized * local_forward;
bearing = (bearing + 1) * 0.5f;
if (bearing > 0.8f)
{
if (m_Random == true)
{
return (Stuff::Random::GetFraction());
}
return (1);
}
return (0);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Evaluator_ToSideOf -- used as a last resort when we can't find a decent spot to move to
//
Evaluator_ToSideOf::Evaluator_ToSideOf(FromWho who,
bool to_side)
: m_Who(who)
, m_ToSide(to_side)
{
Verify(m_Who != FROM_ALL_ENEMIES);
Verify(m_Who != FROM_NEAREST_ENEMY);
}
Fuzzy Evaluator_ToSideOf::Evaluate(TacticInterface& i,
const enemies_list& enemies,
const Stuff::Point3D& point) const
{
Stuff::LinearMatrix4D relative_to;
if (m_Who == FROM_SELF)
{
if (GetEvaluationState() == true)
{
relative_to = i.GetLastMoveOrderPosition();
}
else
{
relative_to = i.GetSelf().GetLocalToWorld();
}
}
else
{
relative_to = i.GetTarget().GetLocalToWorld();
Stuff::Point3D future_pos;
i.GetTarget().EstimateFuturePosition(&future_pos,prediction_time,false);
relative_to.BuildTranslation(future_pos);
}
Stuff::Point3D delta;
delta.Subtract(point,(Stuff::Point3D)relative_to);
if (delta.GetLengthSquared() == 0)
{
return (1);
}
Stuff::UnitVector3D delta_normalized(delta);
Stuff::UnitVector3D local_forward;
relative_to.GetLocalForwardInWorld(&local_forward);
// TODO: is this correct, or the inverse?
Stuff::Scalar bearing = delta_normalized * local_forward;
if (bearing < 0)
{
bearing = -bearing;
}
if (m_ToSide == true)
{
bearing = 1 - bearing;
}
if (bearing > 1)
{
return (1);
}
if (bearing < 0)
{
return (0);
}
return (bearing);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Generator_EscapeRegion -- used as a last resort when we can't find a decent spot to move to
//
unsigned int Generator_EscapeRegion::Iterations() const
{
return (0);
}
void Generator_EscapeRegion::Generate(TacticInterface& i,
point_list& points,
int iteration) const
{
Stuff::Point3D my_pos = (Stuff::Point3D)i.GetSelf().GetLocalToWorld();
{for (int i = 0;
i < 40;
++i)
{
Stuff::Point3D p = my_pos;
p.x += (Stuff::Random::GetFraction() * 200) - 100;
p.y += (Stuff::Random::GetFraction() * 200) - 100;
p.z += (Stuff::Random::GetFraction() * 200) - 100;
points.push_back(p);
}}
}
bool Generator_EscapeRegion::IgnoreMissionBounds(TacticInterface& i) const
{
return (true);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Generator_Circle
//
Generator_Circle::Generator_Circle(FromWho who,
Stuff::Scalar magnitude,
Stuff::Scalar points_on_circumference,
Stuff::Scalar shrink_rate)
: m_Who(who)
, m_Origin(0,0,0)
, m_Magnitude(magnitude)
, m_ShrinkRate(0.8f)
, m_Points(points_on_circumference)
{
Stuff::Point3D pos;
Verify(m_Points > 0);
Verify(who != FROM_ALL_ENEMIES);
}
Generator_Circle::Generator_Circle(const Stuff::Point3D& origin,
Stuff::Scalar magnitude,
Stuff::Scalar points_on_circumference,
Stuff::Scalar shrink_rate)
: m_Who(INVALID) // TODO: fix this -- it's a kludge
, m_Origin(origin)
, m_Magnitude(magnitude)
, m_ShrinkRate(shrink_rate)
, m_Points(points_on_circumference)
{
Stuff::Point3D pos;
Verify(m_Points > 0);
}
unsigned int Generator_Circle::Iterations() const
{
return (3);
}
Stuff::Point3D GetCircleOrigin(TacticInterface& i, FromWho who, const Stuff::Point3D& alternate_origin)
{
switch (who)
{
case FROM_SELF:
{
return ((Stuff::Point3D)i.GetSelf().GetLocalToWorld());
}
case FROM_TARGET:
{
Stuff::Point3D rv;
rv = i.GetTarget().EstimateFuturePosition(&rv,prediction_time,false);
return (rv);
}
case FROM_NEAREST_ENEMY:
{
MechWarrior4::MWObject* nearest = i.GetNearest(true);
if (nearest == 0)
{
return ((Stuff::Point3D)i.GetSelf().GetLocalToWorld());
}
Stuff::Point3D rv;
rv = nearest->EstimateFuturePosition(&rv,prediction_time,false);
return (rv);
}
}
return (alternate_origin);
}
void Generator_Circle::Generate(TacticInterface& i,
point_list& points,
int iteration) const
{
Stuff::Point3D pos = GetCircleOrigin(i,m_Who,m_Origin);
Stuff::Point3D forward = pos;
forward.x += 1;
Stuff::Scalar increment = (Stuff::Pi * 2) / (Stuff::Scalar)m_Points;
Stuff::Scalar magnitude = m_Magnitude;
{for (int i = 0;
i < iteration;
++i)
{
magnitude *= m_ShrinkRate;
}}
Stuff::Scalar random_angle_offset = Stuff::Random::GetFraction() * Stuff::Pi;
{for (Stuff::Scalar i = 0;
i <= Stuff::Pi * 2;
i += increment)
{
Stuff::Point3D new_point(RotateVector(pos,forward,i + random_angle_offset,magnitude));
points.push_back(new_point);
}}
}
bool Generator_Circle::IgnoreMissionBounds(TacticInterface& i) const
{
return (PointIsOutsideMissionBounds(GetCircleOrigin(i,m_Who,m_Origin)));
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Generator_InFrontOf
//
Generator_InFrontOf::Generator_InFrontOf(FromWho who,
Stuff::Scalar length,
Stuff::Scalar random_skew)
: m_Who(who)
, m_Length(length)
, m_RandomSkew(random_skew)
{
}
unsigned int Generator_InFrontOf::Iterations() const
{
return (1);
}
void Generator_InFrontOf::Generate(TacticInterface& i,
point_list& points,
int iteration) const
{
Stuff::Point3D pos;
Stuff::UnitVector3D local_forward;
if (m_Who == TacticInterface::SELF)
{
pos = (Stuff::Point3D)i.GetSelf().EstimateFuturePosition(&pos,prediction_time,false);
i.GetSelf().GetLocalToWorld().GetLocalForwardInWorld(&local_forward);
}
else
{
pos = (Stuff::Point3D)i.GetTarget().EstimateFuturePosition(&pos,prediction_time,false);
i.GetTarget().GetLocalToWorld().GetLocalForwardInWorld(&local_forward);
}
{for (Stuff::Scalar index = 0;
index <= 1;
index += 0.1f)
{
Stuff::Point3D p(local_forward);
p *= m_Length * index;
p += pos;
OffsetTargetPoint(p,m_RandomSkew);
points.push_back(p);
}}
}
bool Generator_InFrontOf::IgnoreMissionBounds(TacticInterface& i) const
{
Stuff::Point3D pos;
if (m_Who == TacticInterface::SELF)
{
return (PointIsOutsideMissionBounds((Stuff::Point3D)i.GetSelf().GetLocalToWorld()));
}
return (PointIsOutsideMissionBounds((Stuff::Point3D)i.GetTarget().GetLocalToWorld()));
}