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.
1123 lines
29 KiB
C++
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
|
|
}
|