#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 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(true,(*i).second)); } }} } return (std::pair(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 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 result(TryGenerator(iface, circle, special_evaluator, minimum_distance, 0, false)); if (result.first == true) { return (result.second); } } Generator_EscapeRegion escape_region; std::pair 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())); }