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.
1013 lines
24 KiB
C++
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()));
|
|
} |