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

1123 lines
29 KiB
C++

#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
}