#include "MW4Headers.hpp" #include "AI_Action.hpp" #include "AI_FireStyle.hpp" #include "AI_SituationalAnalysis.hpp" #include "AI_FireData.hpp" #include "Mech.hpp" #include "JumpJet.hpp" #include "Airplane.hpp" #include "MWMission.hpp" #include "Torso.hpp" #include "Boat.hpp" extern __int64 tCombatAITime_Track; using namespace MW4AI; using namespace MW4AI::Actions; using namespace MW4AI::SituationalAnalysis; namespace MW4AI { extern Stuff::Scalar MinZ, MaxZ, MinX, MaxX; }; bool Actions::g_HelicoptersIgnoreMissionBounds = false; void Actions::Fire(TacticInterface& i, FireStyles::FireStyleID style) { AILOGFUNC("Actions::Fire"); AILOG1(style); i.Fire(style); } const Stuff::Scalar circle_radians = Stuff::Pi_Over_6; const Stuff::Scalar stop_radius = 35.0f; const Stuff::Scalar min_move_radius = 50.0f; const Stuff::Scalar min_movement_score_to_recalc_multiplier = 0.5f; const Stuff::Scalar bad_shot_dodge_time = 5.0f; // TODO: pull ShouldReevaluate() upwards, into Behaviors!!! ... bool ShouldReevaluate(MW4AI::TacticInterface& i, Stuff::Scalar distance) { AILOGFUNC("ShouldReevaluate"); AILOG1(distance); if (i.CanMove() == false) { LOG_AND_RETURN(false); } if (i.ShouldForceDestinationRecalc() == true) { LOG_AND_RETURN(true); } if (i.MoveRequestPending() == true) { LOG_AND_RETURN(false); } if (i.GetDistanceFromDestinationSquared() < (distance * distance)) { LOG_AND_RETURN(true); } Stuff::Point3D last_move_dest; if (i.GetLastMoveDest(last_move_dest) == false) { LOG_AND_RETURN(true); } if (i.MovedWithin(5.0f) == false) { LOG_AND_RETURN(true); } LOG_AND_RETURN(false); } inline bool MoveTypeFlagToBool(TacticInterface& i, const Stuff::Point3D& dest, MoveType move_type) { switch (move_type) { case MOVE_FORWARD: { return (true); } case MOVE_BACKWARD: { return (false); } case MOVE_TO_FACE_TARGET: { return (ShouldMoveForwardToPoint(i,dest)); } } Verify(!"Move type flag not found"); return (true); } inline bool MovingForwardIsEasier(const Stuff::LinearMatrix4D& matrix, const Stuff::Point3D& dest) { if (GetSquaredDistToMatrixForward(dest,matrix) < GetSquaredDistToMatrixBackward(dest,matrix)) { return (false); } return (true); } inline void FindEscapeMoveDest(MW4AI::TacticInterface& i, MoveType move_type) { Stuff::Scalar radius(stop_radius + 30.0f); Generator_Circle generator(FROM_SELF,radius); Evaluator_Random evaluator; Stuff::Point3D dest(Evaluate(i,generator,evaluator,radius)); if (i.PointIsValid(dest,true) == true) { if ((move_type != MOVE_FORWARD) && (MovingForwardIsEasier(i.GetSelf().GetLocalToWorld(),dest) == false)) { move_type = MOVE_BACKWARD; } i.MoveTo(dest,MoveTypeFlagToBool(i,dest,move_type)); i.SetMovementScore(1.0f); } } void FindMoveDest(MW4AI::TacticInterface& i, const MW4AI::SituationalAnalysis::RegionGenerator& generator, const MW4AI::SituationalAnalysis::PointEvaluator& evaluator, Stuff::Scalar radius, MoveType move_type) { if (i.RecentPathsFailed() == false) { Stuff::Point3D dest(Evaluate(i,generator,evaluator,radius)); Fuzzy score(EvaluatePoint(i,dest,evaluator)); if (i.PointIsValid(dest) == true) { i.MoveTo(dest,MoveTypeFlagToBool(i,dest,move_type)); i.SetMovementScore(score); return; } } Scalar radius2 = i.GetLeashRadius(); if (radius2 != 0) { radius2 *= 0.9f; Generator_Circle generator2(i.GetLeashCenter(),radius2); Stuff::Point3D dest(Evaluate(i,generator2,evaluator,radius2)); Fuzzy score(EvaluatePoint(i,dest,evaluator)); if (i.PointIsValid(dest) == true) { i.MoveTo(dest,MoveTypeFlagToBool(i,dest,move_type)); i.SetMovementScore(score); return; } } FindEscapeMoveDest(i,move_type); } void Actions::ApproachTarget(TacticInterface& i, MoveType move_type) { AILOGFUNC("Actions::ApproachTarget"); if (ShouldReevaluate(i,stop_radius) == false) { return; } Evaluator_DistanceFrom evaluator(FROM_SELF,0,800,NEAREST); Generator_Circle generator(FROM_TARGET,25.0f); FindMoveDest(i,generator,evaluator,min_move_radius,move_type); } void Actions::ApproachTarget(TacticInterface& i, Stuff::Scalar when) { AILOGFUNC("Actions::ApproachTarget"); AILOG1(when); Stuff::Point3D target_pos; i.GetTarget().EstimateFuturePosition(&target_pos,(Scalar)when,true); Evaluator_DistanceFrom evaluator(FROM_SELF,0,800,FARTHEST); Generator_Circle generator(target_pos,10.0f); FindMoveDest(i,generator,evaluator,min_move_radius,MOVE_TO_FACE_TARGET); } void Actions::FleeTarget(TacticInterface& i) { AILOGFUNC("Actions::FleeTarget"); if (ShouldReevaluate(i,stop_radius) == false) { return; } Evaluator_DistanceFrom distance_from_self(FROM_SELF,0,800,NEAREST); Evaluator_DistanceFrom distance_from_enemies(FROM_ALL_ENEMIES,0,800,FARTHEST); Evaluator_InFrontOf in_front_of(FROM_SELF,true); Evaluator_WeightedAverage evaluator(0.35f,distance_from_self, 0.15f,in_front_of, 0.5f,distance_from_enemies); Generator_Circle generator(FROM_SELF,stop_radius + 45); FindMoveDest(i,generator,evaluator,min_move_radius,MOVE_FORWARD); } void Actions::FleeNearest(TacticInterface& i, Stuff::Scalar distance) { AILOGFUNC("Actions::FleeNearest"); AILOG(distance); if (ShouldReevaluate(i,stop_radius) == false) { return; } Evaluator_DistanceFrom evaluator(FROM_NEAREST_ENEMY,0,800,FARTHEST); if (distance < min_move_radius + 10) { distance = min_move_radius + 10; } Generator_Circle generator(FROM_SELF,distance); FindMoveDest(i,generator,evaluator,min_move_radius,MOVE_TO_FACE_TARGET); } void Actions::Disengage(TacticInterface& i, Stuff::Scalar distance) { AILOGFUNC("Actions::Disengage"); AILOG(distance); MechWarrior4::MWObject* nearest = i.GetNearest(); if (nearest == 0) { GoToDistanceFromTarget(i,distance,MOVE_TO_FACE_TARGET); return; } Stuff::LinearMatrix4D my_matrix(i.GetSelf().GetLocalToWorld()); Stuff::Point3D my_pos(my_matrix); Stuff::UnitVector3D unit_forward; Stuff::UnitVector3D unit_backward; my_matrix.GetLocalForwardInWorld(&unit_forward); my_matrix.GetLocalBackwardInWorld(&unit_backward); Stuff::Point3D forward(unit_forward); forward *= distance; forward += my_pos; Stuff::Point3D backward(unit_backward); backward *= distance; backward += my_pos; Stuff::Point3D nearest_pos(nearest->GetLocalToWorld()); Stuff::Point3D dest; bool move_forward; if (GetSquaredDistToMatrixForward(nearest_pos,my_matrix) < GetSquaredDistToMatrixBackward(nearest_pos,my_matrix)) { dest = backward; move_forward = false; } else { dest = forward; move_forward = true; } if (GetLengthSquared(dest,i.GetMoveDest()) < 12.0f * 12.0f) { return; } if (i.PointIsValid(dest,true) == false) { GoToDistanceFromTarget(i,distance,MOVE_TO_FACE_TARGET); return; } i.MoveTo(dest,move_forward,true,nearest); i.ThrottleOverride(6.0f,true); } void Actions::GoToDistanceFromTarget(TacticInterface& i, Stuff::Scalar distance, MoveType move_type) { AILOGFUNC("Actions::GoToDistanceFromTarget"); AILOG2(distance,move_type); if (ShouldReevaluate(i,stop_radius) == false) { return; } Evaluator_DistanceFrom distance_from_self(FROM_SELF,0,800,NEAREST); Evaluator_DistanceFrom distance_from_enemies(FROM_ALL_ENEMIES,0,800,FARTHEST); Evaluator_WeightedAverage evaluator(0.7f,distance_from_self, 0.3f,distance_from_enemies); Generator_Circle generator(FROM_TARGET,distance); FindMoveDest(i,generator,evaluator,min_move_radius,move_type); } void Actions::GoToDistanceFromNearestEnemy(TacticInterface& i, Stuff::Scalar distance, MoveType move_type) { AILOGFUNC("Actions::GoToDistanceFromNearestEnemy"); AILOG2(distance,move_type); if (ShouldReevaluate(i,stop_radius) == false) { return; } Evaluator_DistanceFrom distance_from_self(FROM_SELF,10,distance * 1.5f,NEAREST); Evaluator_DistanceFrom distance_from_enemies(FROM_ALL_ENEMIES,10,distance * 1.5f,FARTHEST); Evaluator_WeightedAverage evaluator(0.5f,distance_from_self, 0.5f,distance_from_enemies); Generator_Circle generator(FROM_NEAREST_ENEMY,distance); FindMoveDest(i,generator,evaluator,min_move_radius,move_type); } void Actions::GoToOtherSideOfTarget(TacticInterface& i, Stuff::Scalar distance) { AILOGFUNC("Actions::GoToOtherSideOfTarget"); AILOG1(distance); if (ShouldReevaluate(i,stop_radius) == false) { return; } Evaluator_DistanceFrom far_from_self(FROM_SELF,0,distance * 2,FARTHEST); Evaluator_DistanceFrom far_from_enemies(FROM_ALL_ENEMIES,0,distance * 2,FARTHEST); Evaluator_WeightedAverage evaluator(0.7f,far_from_self, 0.3f,far_from_enemies); Generator_Circle generator(FROM_TARGET,distance); FindMoveDest(i,generator,evaluator,min_move_radius,MOVE_TO_FACE_TARGET); } void Actions::StopIfMoving(TacticInterface& i) { AILOGFUNC("Actions::StopIfMoving"); if (i.Moving() == true) { i.Stop(); } } void Actions::Circle(TacticInterface& i, MoveType move_type, Stuff::Scalar distance) { AILOGFUNC("Actions::Circle"); AILOG2(move_type,distance); if (ShouldReevaluate(i,stop_radius) == false) { return; } Evaluator_DistanceFrom near_self(FROM_SELF,0,500,NEAREST); Evaluator_InFrontOf in_front_of(FROM_SELF,true); Evaluator_DistanceFrom distance_from_enemies(FROM_ALL_ENEMIES,0,500,FARTHEST); Evaluator_WeightedAverage evaluator(0.5f,near_self, 0.25f,in_front_of, 0.25f,distance_from_enemies); if (distance == 0) { distance = GetApproximateLength((Stuff::Point3D)i.GetSelf().GetLocalToWorld(), (Stuff::Point3D)i.GetTarget().GetLocalToWorld()); } Generator_Circle generator(FROM_TARGET,distance,7); FindMoveDest(i,generator,evaluator,min_move_radius,move_type); } void Actions::CircleTargetNearEnemies(TacticInterface& i, Stuff::Scalar distance) { AILOGFUNC("Actions::CircleTargetNearEnemies"); if (ShouldReevaluate(i,stop_radius) == false) { return; } Evaluator_DistanceFrom near_self(FROM_SELF,0,200,NEAREST); Evaluator_InFrontOf in_front_of(FROM_SELF,true); Evaluator_DistanceFrom distance_from_enemies(FROM_ALL_ENEMIES,0,200,NEAREST); Evaluator_WeightedAverage evaluator(0.4f,near_self, 0.2f,in_front_of, 0.4f,distance_from_enemies); Generator_Circle generator(FROM_TARGET,distance,12.0f,0.6f); FindMoveDest(i,generator,evaluator,min_move_radius,MOVE_FORWARD); } void Actions::CircleRandomly(TacticInterface& i) { AILOGFUNC("Actions::CircleRandomly"); if (ShouldReevaluate(i,stop_radius) == false) { return; } Stuff::Scalar distance = GetApproximateLength((Stuff::Point3D)i.GetSelf().GetLocalToWorld(), (Stuff::Point3D)i.GetTarget().GetLocalToWorld()); Evaluator_DistanceFrom near_self(FROM_SELF,0,1000,NEAREST); Evaluator_DistanceFrom distance_from_enemies(FROM_ALL_ENEMIES,0,800,FARTHEST); Evaluator_WeightedAverage evaluator(0.8f,near_self, 0.2f,distance_from_enemies); Generator_Circle generator(FROM_TARGET,distance,5); FindMoveDest(i,generator,evaluator,min_move_radius,MOVE_TO_FACE_TARGET); } void Actions::CircleFloat(TacticInterface& i, Stuff::Scalar distance) { AILOGFUNC("Actions::CircleRandomly"); if (i.GetSelf().IsDerivedFrom(Airplane::DefaultData) == false) { return; } Stuff::Point3D last_move_dest; i.GetLastMoveDest(last_move_dest,true); Stuff::Point3D my_pos(i.GetSelf().GetLocalToWorld()); last_move_dest.y = 0; my_pos.y = 0; if ((last_move_dest.GetLengthSquared() == 0) || (GetApproximateLength(my_pos,last_move_dest) < 5.0f)) { Generator_Circle generator(FROM_TARGET,distance,9); SituationalAnalysis::point_list points; generator.Generate(i,points); // TODO: move all this elsewhere Stuff::Scalar best_score(-1); SituationalAnalysis::point_list::const_iterator best = points.end(); Stuff::LinearMatrix4D relative_to; if ((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(); } {for (SituationalAnalysis::point_list::const_iterator i_points = points.begin(); i_points != points.end(); ++i_points) { Stuff::Scalar distance = GetApproximateLength(*i_points,(Stuff::Point3D)(i.GetSelf().GetLocalToWorld())); if (distance < 50.0f) { continue; } if (distance < 0) { distance = 0; } if (distance > 500) { distance = 500; } Stuff::Scalar bearing(YawToPoint(relative_to,*i_points)); bearing = Stuff::Fabs(bearing); Stuff::Scalar score = (500 - distance) + (500 * bearing); if (score > best_score) { if (((*i_points).x <= MinX) || ((*i_points).x >= MaxX) || ((*i_points).z <= MinZ) || ((*i_points).z >= MaxZ)) { continue; } if (g_HelicoptersIgnoreMissionBounds == false) { 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(*i_points) != MWMission::InMissionArea) { continue; } } best_score = score; best = i_points; } }} if (best == points.end()) { return; } i.FloatToPoint(*best); } } void Actions::CircleToBehindTarget(TacticInterface& i, Stuff::Scalar maximum) { AILOGFUNC("Actions::CircleToBehindTarget"); AILOG1(maximum); if (ShouldReevaluate(i,stop_radius) == false) { return; } Stuff::Scalar distance = GetApproximateLength((Stuff::Point3D)i.GetAttackOrderPosition(), (Stuff::Point3D)i.GetTarget().GetLocalToWorld()); if (distance > maximum) { distance = maximum; } Evaluator_InFrontOf behind_enemy(FROM_TARGET,false,true); Evaluator_DistanceFrom near_self2(FROM_SELF,0,distance * 2,NEAREST); Evaluator_DistanceFrom distance_from_enemies2(FROM_ALL_ENEMIES,0,distance * 2,FARTHEST); Evaluator_WeightedAverage evaluator2(0.5f,behind_enemy, 0.4f,near_self2, 0.1f,distance_from_enemies2); Generator_Circle generator(FROM_TARGET,distance); FindMoveDest(i,generator,evaluator2,min_move_radius,MOVE_TO_FACE_TARGET); } // TODO: share code here with CircleToBehindTarget() ... these are both very similar void Actions::CircleToFrontOfTarget(TacticInterface& i, Stuff::Scalar maximum) { AILOGFUNC("Actions::CircleToFrontOfTarget"); AILOG1(maximum); if (ShouldReevaluate(i,stop_radius) == false) { return; } Stuff::Scalar distance = GetApproximateLength((Stuff::Point3D)i.GetAttackOrderPosition(), (Stuff::Point3D)i.GetTarget().GetLocalToWorld()); if (distance > maximum) { distance = maximum; } Stuff::Scalar distance2 = GetApproximateLength((Stuff::Point3D)i.GetSelf().GetLocalToWorld(), (Stuff::Point3D)i.GetTarget().GetLocalToWorld()); Evaluator_InFrontOf in_front_of(FROM_TARGET,true,true); Evaluator_DistanceFrom near_self2(FROM_SELF,0,distance2 * 2,NEAREST); Evaluator_DistanceFrom distance_from_enemies2(FROM_ALL_ENEMIES,0,distance2 * 2,FARTHEST); Evaluator_WeightedAverage evaluator2(0.5f,in_front_of, 0.4f,near_self2, 0.1f,distance_from_enemies2); Generator_Circle generator(FROM_TARGET,distance); FindMoveDest(i,generator,evaluator2,min_move_radius,MOVE_TO_FACE_TARGET); } void Actions::DoFastCircle(TacticInterface& i, FastCircleMoveType move_type) { AILOGFUNC("Actions::DoFastCircle"); if (ShouldReevaluate(i,stop_radius * 0.75f) == false) { return; } Generator_Circle generator(FROM_SELF,(stop_radius * 2.0f) + (Stuff::Random::GetFraction() * stop_radius),9); switch (move_type) { case MOVE_CLOSER: { Evaluator_45DegreesInFront evaluator_45(false); Evaluator_DistanceFrom evaluator_distance(FROM_TARGET,20,600,NEAREST); Evaluator_WeightedAverage evaluator(0.5f,evaluator_45, 0.5f,evaluator_distance); FindMoveDest(i,generator,evaluator,min_move_radius,MOVE_FORWARD); break; } case MOVE_FARTHER: { Evaluator_45DegreesInFront evaluator_45(false); Evaluator_DistanceFrom evaluator_distance(FROM_TARGET,20,600,FARTHEST); Evaluator_WeightedAverage evaluator(0.5f,evaluator_45, 0.5f,evaluator_distance); FindMoveDest(i,generator,evaluator,min_move_radius,MOVE_FORWARD); break; } case MOVE_RANDOM: { Evaluator_45DegreesInFront evaluator_45(true); Evaluator_DistanceFrom evaluator_distance(FROM_TARGET,20,600,NEAREST); Evaluator_WeightedAverage evaluator(0.8f,evaluator_45, 0.2f,evaluator_distance); FindMoveDest(i,generator,evaluator,min_move_radius,MOVE_FORWARD); break; } default: { Verify(!"Invalid enumerated type."); break; } } } void Actions::Dodge(TacticInterface& i, Stuff::Scalar distance, MoveType move_type) { AILOGFUNC("Actions::Dodge"); AILOG1(distance); if (ShouldReevaluate(i,stop_radius) == false) { return; } Evaluator_DistanceFrom distance_from_self(FROM_SELF,10,400,NEAREST); Evaluator_ToSideOf to_side_of_target(FROM_TARGET,true); Evaluator_DistanceFrom distance_from_enemies(FROM_ALL_ENEMIES,10,400,FARTHEST); Evaluator_WeightedAverage evaluator(0.6f,to_side_of_target, 0.25f,distance_from_self, 0.15f,distance_from_enemies); Generator_Circle generator(FROM_TARGET,distance * 1.5f); FindMoveDest(i,generator,evaluator,min_move_radius,move_type); } void Actions::MoveForward(TacticInterface& i, Stuff::Scalar distance) { AILOGFUNC("Actions::MoveForward"); AILOG1(distance); if (i.RecentPathsFailed() == true) { FindEscapeMoveDest(i,MOVE_FORWARD); return; } if (ShouldReevaluate(i,stop_radius) == false) { return; } Stuff::UnitVector3D forward_vector; i.GetSelf().GetLocalToWorld().GetLocalForwardInWorld(&forward_vector); Stuff::Point3D move_to(forward_vector); move_to *= distance; move_to += (Stuff::Point3D)i.GetSelf().GetLocalToWorld(); if (i.PointIsValid(move_to) == true) { i.MoveTo(move_to); } } void Actions::Crouch(TacticInterface& i) { AILOGFUNC("Actions::Crouch"); i.Crouch(); } void Actions::ThrottleOverride(TacticInterface& i) { AILOGFUNC("Actions::ThrottleOverride"); i.ThrottleOverride(4.0f); } void Actions::Jump(TacticInterface& i) { AILOGFUNC("Actions::Jump"); i.Jump(); } void Actions::FindBetterLineOfFire(TacticInterface& i) { AILOGFUNC("Actions::FindBetterLineOfFire"); if (ShouldReevaluate(i,stop_radius) == false) { return; } Stuff::Scalar distance_from_target = GetApproximateLength((Stuff::Point3D)i.GetSelf().GetLocalToWorld(), (Stuff::Point3D)i.GetTarget().GetLocalToWorld()); Evaluator_DistanceFrom distance_from_self(FROM_SELF,20,distance_from_target * 2,NEAREST); Evaluator_DistanceFrom distance_from_enemies(FROM_ALL_ENEMIES,20,distance_from_target * 2,FARTHEST); Evaluator_WeightedAverage evaluator(0.7f,distance_from_self, 0.3f,distance_from_enemies); FireData fire_data(i.GetTarget(), i.GetTarget().GetLineOfSight(), (Stuff::Point3D)i.GetSelf().GetLocalToWorld()); fire_data.Project(); Stuff::Scalar line_length = fire_data.GetLine().m_length; if ((line_length > 20) && (line_length < distance_from_target * 0.7)) { Stuff::Point3D delta; delta.Subtract((Stuff::Point3D)i.GetSelf().GetLocalToWorld(), (Stuff::Point3D)i.GetTarget().GetLocalToWorld()); delta *= (line_length / distance_from_target); delta += (Stuff::Point3D)i.GetTarget().GetLocalToWorld(); if (i.PointIsValid(delta) == true) { i.MoveTo(delta); return; } } Generator_Circle generator(FROM_TARGET,distance_from_target * 0.7f); FindMoveDest(i,generator,evaluator,min_move_radius,MOVE_TO_FACE_TARGET); } void Actions::GoBehindTarget(TacticInterface& i, Stuff::Scalar min_distance) { AILOGFUNC("Actions::GoBehindTarget"); if (ShouldReevaluate(i,stop_radius) == false) { return; } Stuff::Scalar distance_from_target = GetApproximateLength((Stuff::Point3D)i.GetSelf().GetLocalToWorld(), (Stuff::Point3D)i.GetTarget().GetLocalToWorld()); Evaluator_InFrontOf behind_enemy(FROM_TARGET,false,true); Evaluator_DistanceFrom distance_from_enemies(FROM_ALL_ENEMIES,10,distance_from_target * 2,FARTHEST); Evaluator_WeightedAverage evaluator(0.8f,behind_enemy, 0.2f,distance_from_enemies); Stuff::Scalar desired_distance = distance_from_target * 0.5f; if (desired_distance < min_distance) { desired_distance = min_distance; } Generator_Circle generator(FROM_TARGET, desired_distance); FindMoveDest(i,generator,evaluator,min_move_radius,MOVE_TO_FACE_TARGET); } // TODO: share code here with GoBehindTarget() void Actions::GoInFrontOfTarget(TacticInterface& i, Stuff::Scalar min_distance) { AILOGFUNC("Actions::GoInFrontOfTarget"); if (ShouldReevaluate(i,stop_radius) == false) { return; } Stuff::Scalar distance_from_target = GetApproximateLength((Stuff::Point3D)i.GetSelf().GetLocalToWorld(), (Stuff::Point3D)i.GetTarget().GetLocalToWorld()); Evaluator_InFrontOf in_front_of(FROM_TARGET,true,true); Evaluator_DistanceFrom distance_from_enemies(FROM_ALL_ENEMIES,10,distance_from_target * 2,FARTHEST); Evaluator_WeightedAverage evaluator(0.8f,in_front_of, 0.2f,distance_from_enemies); Stuff::Scalar desired_distance = distance_from_target * 0.5f; if (desired_distance < min_distance) { desired_distance = min_distance; } Generator_Circle generator(FROM_TARGET, desired_distance); FindMoveDest(i,generator,evaluator,min_move_radius,MOVE_TO_FACE_TARGET); } void Actions::PatrolWithinRadiusOfAttackOrderPosition(TacticInterface& i, Stuff::Scalar inner_radius, Stuff::Scalar outer_radius) { AILOGFUNC("Actions::PatrolWithinRadiusOfAttackOrderPosition"); AILOG2(inner_radius,outer_radius); if (ShouldReevaluate(i,stop_radius) == false) { return; } Evaluator_InFrontOf behind_enemy(FROM_TARGET,false,true); Evaluator_DistanceFrom distance_from_self(FROM_SELF,10,outer_radius * 2,FARTHEST); Evaluator_WeightedAverage evaluator(0.4f,behind_enemy, 0.6f,distance_from_self); Stuff::Scalar distance = NormalizedInterpolation(Stuff::Random::GetFraction(),inner_radius,outer_radius); Generator_Circle generator((Stuff::Point3D)i.GetAttackOrderPosition(),distance); FindMoveDest(i,generator,evaluator,min_move_radius,MOVE_TO_FACE_TARGET); } void Actions::ForceDestinationRecalc(TacticInterface& i) { AILOGFUNC("Actions::ForceDestinationRecalc"); i.ForceDestinationRecalc(); } void Actions::RamTarget(TacticInterface& i, Stuff::Scalar recalc_tolerance) { AILOGFUNC("Actions::RamTarget"); AILOG(recalc_tolerance); Verify(i.GetSelf_AsVehicle() != 0); if (i.RecentPathsFailed() == true) { FindEscapeMoveDest(i,MOVE_FORWARD); return; } Stuff::Scalar target_velocity(0); if (i.GetTarget_AsVehicle() != 0) { target_velocity = i.GetTarget_AsVehicle()->currentSpeedMPS; } i.ThrottleOverride(5.0f); Stuff::Scalar my_speed((i.GetSelf_AsVehicle()->GetGameModel()->maxSpeed + i.GetSelf_AsVehicle()->currentSpeedMPS) * 0.5f); // average my current speed with my max speed for a good guess Stuff::Scalar time_to_collide = TimeToCollide(i.GetTarget().GetLocalToWorld(), target_velocity, (Stuff::Point3D)(i.GetSelf().GetLocalToWorld()), my_speed); Stuff::Point3D new_pos; if (time_to_collide > 0) { if (time_to_collide > 8) { time_to_collide = 8; } i.GetTarget().EstimateFuturePosition(&new_pos,time_to_collide,false); } else { new_pos = (Stuff::Point3D)i.GetTarget().GetLocalToWorld(); } const Stuff::Point3D old_pos(i.GetMoveDest()); if ((i.ShouldForceDestinationRecalc() == true) || (GetLengthSquared(new_pos,old_pos) > recalc_tolerance * recalc_tolerance)) { i.MoveTo(new_pos,true,true,&(i.GetTarget())); } } void Actions::JumpToHeight(TacticInterface& i, Stuff::Scalar height) { if (i.GetSelf().IsDerivedFrom(MechWarrior4::Mech::DefaultData) == false) { return; } MechWarrior4::Mech* mech = Cast_Object(MechWarrior4::Mech*,&i.GetSelf()); if (mech->GetJumpJet() == 0) { return; } const Stuff::Point3D my_pos(i.GetSelf().GetLocalToWorld()); BYTE tempmaterial; const Stuff::Scalar current_height = my_pos.y - GetMapY(my_pos.x,my_pos.z,&i.GetSelf(),tempmaterial); const Stuff::Scalar vertical_velocity = i.GetSelf().worldSpaceVelocity.linearMotion.y; if (mech->GetJumpJet()->executionState->GetState() != JumpJet::ExecutionStateEngine::JumpingState) { if ((current_height < height) || (vertical_velocity < -25.0f) || (current_height + (vertical_velocity * 2.0f) < height)) { if (vertical_velocity < 20.0f) { Stuff::Scalar desired_time = height - current_height; desired_time += ((20.0f - vertical_velocity) * 2); desired_time /= 100; if (desired_time > 0.4f) { desired_time = 0.4f; } if (desired_time < 0.25f) { desired_time = 0.25f; } i.Jump(desired_time); } } } } void Actions::Track(TacticInterface& i, TrackTarget target, bool fTrack, bool fTurn, bool fPitch) { AILOGFUNC("Actions::Track"); AILOG3(target,fTrack,fTurn); TIME_FUNCTION(tCombatAITime_Track); if (fTrack == true) { fTrack = i.CanTrack(); } if (fTurn == true) { fTurn = i.CanTurn(); } if ((fTrack == false) && (fTurn == false) && (fPitch == false)) { return; } MechWarrior4::MWObject* who_to_track = 0; switch (target) { case TO_TARGET: { who_to_track = &(i.GetTarget()); break; } case TO_NEAREST_ENEMY: case TO_NEAREST_ENEMY_OR_TARGET: { Adept::Entity* nearest_enemy = i.GetNearest(true); if (nearest_enemy != 0) { Check_Object(nearest_enemy); if (nearest_enemy->IsDerivedFrom(MechWarrior4::MWObject::DefaultData) == true) { who_to_track = Cast_Object(MechWarrior4::MWObject*,nearest_enemy); } } else { if (target == TO_NEAREST_ENEMY_OR_TARGET) { who_to_track = &(i.GetTarget()); } } break; } } if (who_to_track == 0) { Stuff::Point3D my_pos(i.GetSelf().GetLocalToWorld()); Stuff::Point3D delta; delta.Subtract(my_pos,(Stuff::Point3D)i.GetTarget().GetLocalToWorld()); delta += my_pos; if (fTrack == true) { i.TrackTo(delta); } if (fTurn == true) { i.TurnTo(delta); } if ((fPitch == true) && (i.GetSelf_AsVehicle() != 0)) { i.PitchTo(delta); } return; } Stuff::Point3D point(who_to_track->GetLineOfSight()); if (fTrack == true) { i.TrackTo(point); } if (fTurn == true) { const Stuff::Scalar current_vulnerable_component = i.GetCurrentVulnerableComponent(); if (current_vulnerable_component != 0) { Stuff::Point3D my_pos((Stuff::Point3D)i.GetSelf().GetLocalToWorld()); my_pos.y = point.y; Stuff::Point3D delta; delta.Subtract(my_pos,point); Stuff::Point3D new_delta(0,0,0); if (current_vulnerable_component < 0) { my_pos.x += delta.z; my_pos.z -= delta.x; } else { my_pos.x -= delta.z; my_pos.z += delta.x; } i.TurnTo(my_pos,false); } else { i.TurnTo(point); } } if ((fPitch == true) && (i.GetSelf_AsVehicle() != 0)) { i.PitchTo(point); } } bool Actions::ShouldMoveForwardToPoint(TacticInterface& i, const Stuff::Point3D& dest) { if ((i.NumLegsDestroyed() > 0) || (i.GetSelf().IsDerivedFrom(Boat::DefaultData) == true)) { return (true); // if gimped, we should move forwards } const Stuff::Point3D my_pos(i.GetSelf().GetLocalToWorld()); const Stuff::Point3D target_pos(i.GetTarget().GetLocalToWorld()); const Stuff::Scalar pos_to_dest = GetApproximateLength(my_pos,dest); const Stuff::Scalar dest_to_target = GetApproximateLength(target_pos,dest); const Stuff::Scalar pos_to_target = GetApproximateLength(target_pos,my_pos); if ((pos_to_dest > pos_to_target) && (pos_to_dest > dest_to_target)) { return (true); // if the point is way off on the other side of my target, move forwards } if (dest_to_target > (pos_to_target + 30.0f)) { return (false); // if the point is significantly farther than I am, walk backwards } return (true); // otherwise, the point is closer -- walk forwards }