1
0
KF2-Dev-Scripts/GameFramework/Classes/GameAICmd_Hover_MoveToGoal_Mesh.uc

637 lines
16 KiB
Ucode
Raw Permalink Normal View History

2020-12-13 15:01:13 +00:00
/**
* Copyright 1998-2013 Epic Games, Inc. All Rights Reserved.
*/
class GameAICmd_Hover_MoveToGoal_Mesh extends GameAICommand;
var transient Actor Find;
var float Radius;
var transient bool bWasFiring;
var float DesiredHoverHeight;
var transient float CurrentHoverHeight;
var float SubGoalReachDist;
/** how close to get to the enemy (only valid of bCompleteMove is TRUE) */
var transient float GoalDistance;
/** current vector destination */
var transient vector IntermediatePoint;
var transient vector LastMovePoint;
var transient int NumMovePointFails;
var int MaxMovePointFails;
var transient Vector FallbackDest;
var transient Actor MoveToActor;
/** location of MoveToActor last time we did pathfinding */
var BasedPosition LastMoveTargetPathLocation;
/** storage of initial desired move location */
var transient vector InitialFinalDestination;
/** is this AI on 'final approach' ( i.e. moving directly to it's end-goal destination )*/
var bool bFinalApproach;
/** TRUE when we are trying to get back on the mesh */
var bool bFallbackMoveToMesh;
/** Simple constructor that pushes a new instance of the command for the AI */
static function bool HoverToGoal( GameAIController AI, Actor InGoal, float InGoalDistance, float InHoverHeight )
{
local GameAICmd_Hover_MoveToGoal_Mesh Cmd;
if( AI != None && AI.Pawn != None && AI.Pawn.bCanFly)
{
Cmd = new(AI) class'GameAICmd_Hover_MoveToGoal_Mesh';
if( Cmd != None )
{
Cmd.GoalDistance = InGoalDistance;
Cmd.MoveToActor = InGoal;
Cmd.InitialFinalDestination = InGoal.GetDestination(AI);
Cmd.DesiredHoverHeight = InHoverHeight;
Cmd.CurrentHoverHeight = InHoverHeight;
AI.PushCommand( Cmd );
return TRUE;
}
}
return FALSE;
}
static function bool HoverToPoint( GameAIController AI, vector InPoint, float InGoalDistance, float InHoverHeight )
{
local GameAICmd_Hover_MoveToGoal_Mesh Cmd;
if( AI != None && AI.Pawn != None && AI.Pawn.bCanFly)
{
Cmd = new(AI) class'GameAICmd_Hover_MoveToGoal_Mesh';
if( Cmd != None )
{
Cmd.GoalDistance = InGoalDistance;
Cmd.MoveToActor = none;
Cmd.InitialFinalDestination = InPoint;
Cmd.DesiredHoverHeight = InHoverHeight;
Cmd.CurrentHoverHeight = InHoverHeight;
AI.PushCommand( Cmd );
return TRUE;
}
}
return FALSE;
}
static function bool HoverBackToMesh( GameAIController AI )
{
local GameAICmd_Hover_MoveToGoal_Mesh Cmd;
if( AI != None && AI.Pawn != None && AI.Pawn.bCanFly)
{
Cmd = new(AI) class'GameAICmd_Hover_MoveToGoal_Mesh';
if( Cmd != None )
{
Cmd.bFallbackMoveToMesh=true;
AI.PushCommand( Cmd );
return TRUE;
}
}
return FALSE;
}
function Pushed()
{
Super.Pushed();
if( bFallbackMoveToMesh )
{
`AILog("Going into breadcrumb fallback state to get back onto navmesh CurLoc:"@Pawn.Location);
GotoState('Fallback_Breadcrumbs');
return;
}
if( !NavigationHandle.ComputeValidFinalDestination(InitialFinalDestination) )
{
`AILog("ABORTING! Final destination"@InitialFinalDestination@"is not reachable! (ComputeValidFinalDestination returned FALSE)");
GotoState('DelayFailure');
}
else if( !NavigationHandle.SetFinalDestination(InitialFinalDestination) )
{
`AILog("ABORTING! Final destination"@InitialFinalDestination@"is not reachable! (SetFinalDestination returned FALSE)");
GotoState('DelayFailure');
}
else
{
GotoState('Moving');
}
}
function Popped()
{
Super.Popped();
// Check for any latent move actions
ClearLatentAction( class'SeqAct_AIMoveToActor', Status != 'Success' );
// clear the route cache to make sure any intermediate claims are destroyed
NavigationHandle.PathCache_Empty();
// explicitly clear velocity/accel as they aren't necessarily
// cleared by the physics code
if( Pawn != None )
{
Pawn.ZeroMovementVariables();
Pawn.DestinationOffset = 0.f;
}
ReachedMoveGoal();
}
function Tick( float DeltaTime )
{
Super.Tick(DeltaTime);
if( ShouldUpdateBreadCrumbs() )
{
NavigationHandle.UpdateBreadCrumbs(Pawn.Location);
}
NavigationHandle.DrawBreadCrumbs();
}
function bool HandlePathObstruction(Actor BlockedBy)
{
// ! intentionally does not pass on to children
MoveTimer = -1.f; // kills latent moveto's
GotoState('Fallback_Breadcrumbs');
return false;
}
state DelayFailure
{
function bool HandlePathObstruction(Actor BlockedBy);
Begin:
Sleep( 0.5f );
Status = 'Failure';
PopCommand( self );
}
state MoveDown `DEBUGSTATE
{
// find a safe altitude to fly to
function vector GetMoveDest()
{
local vector HitLocation;
local vector HitNormal;
local vector Dest;
local actor HitActor;
// find the poly we're above and try to fly to that
if(NavigationHandle.LineCheck(Pawn.Location, Pawn.Location + vect(0.f,0.f,-4096.f),vect(5,5,5),HitLocation,HitNormal))
{
// if we didn't hit the mesh for some reason, trace against geo
HitActor = Trace(HitLocation,HitNormal,Pawn.Location + vect(0,0,-4096.f),Pawn.Location);
if(HitActor == none)
{
`AILog(GetFuncName()@"Could not find surface to adjust height to!");
return Pawn.Location;
}
}
Dest = HitLocation;
Dest.Z += Pawn.GetCollisionHeight() * 1.5f;
return Dest;
}
Begin:
`AILog("Moving down!");
MoveTo(GetMoveDest());
Sleep(1.0f);
GotoState('Moving');
};
function ReEvaluatePath()
{
/* `AILog(GetFuncName()@bReevaluatePath);
if( HasReachedGoal() )
{
Status = 'Success';
PopCommand(self);
}
else if( bReevaluatePath &&
Pawn != None &&
MovePointIsValid() )
{
//debug
`AILog( "Move continued... Goal"@MoveGoal@"Anchor"@Pawn.Anchor);
NavigationHandle.PathCache_Empty();
`AILog(GetFuncName()@"disabling bReevaluatePath");
bReevaluatePath = FALSE;
// Restart the movement state
GotoState( 'Moving', 'Begin' );
}*/
}
/** Check if AI has successfully reached the given goal */
function bool HasReachedGoal()
{
if( Pawn == None )
{
return TRUE;
}
`AILog(GetFuncName()@bFinalApproach@MoveToActor);
if(bFinalApproach && MoveToActor != None)
{
return Pawn.ReachedDestination(MoveToActor);
}
if( BP2Vect( NavigationHandle.FinalDestination ) != vect(0,0,0) )
{
if( VSize(BP2Vect(NavigationHandle.FinalDestination)-Pawn.Location) < GoalDistance )
{
return TRUE;
}
return Pawn.ReachedPoint( BP2Vect(NavigationHandle.FinalDestination), None );
}
return FALSE;
}
function bool ShouldUpdateBreadCrumbs()
{
return true;
}
state Moving `DEBUGSTATE
{
final function float GetMoveDestinationOffset()
{
// return a negative destination offset so we get closer to our points (yay)
if( bFinalApproach )
{
return GoalDistance;
}
else
{
return (SubGoalReachDist - Pawn.GetCollisionRadius());
}
}
CheckMove:
//debug
`AILog("CHECKMOVE TAG");
if( HasReachedGoal() )
{
Goto( 'ReachedGoal' );
}
Begin:
`AILog("BEGIN TAG"@GetStateName());
if( Enemy != none )
{
Radius = Pawn.GetCollisionRadius() + Enemy.GetCollisionRadius();
}
Radius = FMax(Radius, GoalDistance);
NavigationHandle.SetFinalDestination(InitialFinalDestination);
if( NavigationHandle.PointReachable(BP2Vect(NavigationHandle.FinalDestination)) )
{
IntermediatePoint = BP2Vect(NavigationHandle.FinalDestination);
}
else
{
if( MoveToActor != None )
{
// update final dest in case target moved
if( !NavigationHandle.SetFinalDestination(MoveToActor.GetDestination(Outer)) )
{
`AILog("ABORTING! Final destination"@InitialFinalDestination@"is not reachable! (SetFinalDestination returned FALSE)");
Goto('FailedMove');
}
}
if( !GeneratePathToLocation( BP2Vect(NavigationHandle.FinalDestination),GoalDistance, TRUE ) )
{
//debug
`AILog("Couldn't generate path to location"@BP2Vect(NavigationHandle.FinalDestination)@"from"@Pawn.Location);
//`AILog("Retrying with mega debug on");
//NavigationHandle.bDebugConstraintsAndGoalEvals = TRUE;
//NavigationHandle.bUltraVerbosePathDebugging = TRUE;
//GeneratePathToLocation( BP2Vect(NavigationHandle.FinalDestination),GoalDistance, TRUE );
GotoState( 'Fallback_Breadcrumbs' );
}
//debug
`AILog( "Generated path..." );
`AILog( "Found path!" @ `showvar(BP2Vect(NavigationHandle.FinalDestination)), 'Move' );
if( !NavigationHandle.GetNextMoveLocation( IntermediatePoint, SubGoalReachDist ) )
{
//debug
`AILog("Generated path, but couldn't retrieve next move location?");
Goto( 'FailedMove' );
}
}
if( MoveToActor != None )
{
Vect2BP(LastMoveTargetPathLocation, MoveToActor.GetDestination(Outer));
}
while( TRUE )
{
//debug
`AILog( "Still moving to"@IntermediatePoint, 'Loop' );
bFinalApproach = VSizeSq(IntermediatePoint - BP2Vect(NavigationHandle.FinalDestination)) < 1.0;
`AILog("Calling MoveTo -- "@IntermediatePoint);
// if on our final move to an Actor, send it in directly so we account for any movement it does
if( bFinalApproach && MoveToActor != None )
{
`AILog(" - Final approach to" @ MoveToActor $ ", using MoveToward()");
Vect2BP(LastMoveTargetPathLocation, MoveToActor.GetDestination(outer));
NavigationHandle.SetFinalDestination(MoveToActor.GetDestination(outer));
MoveToward(MoveToActor, Enemy, GetMoveDestinationOffset(), FALSE);
}
else
{
// if we weren't given a focus, default to looking where we're going
if( Enemy == None )
{
SetFocalPoint(IntermediatePoint);
}
// use a negative offset so we get closer to our points!
MoveTo(IntermediatePoint, Enemy, GetMoveDestinationOffset() );
}
`AILog("MoveTo Finished -- "@IntermediatePoint);
// if( bReevaluatePath )
// {
// ReEvaluatePath();
// }
if( HasReachedGoal() )
{
Goto( 'CheckMove' );
}
// if we are moving towards a moving target, repath every time we successfully reach the next node
// as that Pawn's movement may cause the best path to change
else if( MoveToActor != None &&
VSize(MoveToActor.GetDestination(Outer) - BP2Vect(LastMoveTargetPathLocation)) > 512.0 )
{
Vect2BP(LastMoveTargetPathLocation, MoveToActor.GetDestination(outer));
`AILog("Repathing because target moved:" @ MoveToActor);
Goto('CheckMove');
}
else if( !NavigationHandle.GetNextMoveLocation( IntermediatePoint, SubGoalReachDist ) )
{
`AILog( "Couldn't get next move location" );
if (!bFinalApproach && ((MoveToActor != None) ? /*NavigationHandle.*/ActorReachable(MoveToActor) : /*NavigationHandle.*/PointReachable(BP2Vect(NavigationHandle.FinalDestination))))
{
`AILog("Target is directly reachable; try direct move");
IntermediatePoint =((MoveToActor != None) ? MoveToActor.GetDestination(outer) : BP2Vect(NavigationHandle.FinalDestination));
Sleep(RandRange(0.1,0.175));
}
else
{
Sleep(0.1f);
`AILog("GetNextMoveLocation returned false, and finaldest is not directly reachable");
Goto('FailedMove');
}
}
else
{
if(VSize(IntermediatePoint-LastMovePoint) < (Pawn.GetCollisionRadius() * 0.1f) )
{
NumMovePointFails++;
// DrawDebugBox(Pawn.Location, vect(2,2,2) * NumMovePointFails, 255, 255, 255, true );
`AILog("WARNING: Got same move location... something's wrong?!"@`showvar(LastMovePoint)@`showvar(IntermediatePoint)@"Delta"@VSize(LastMovePoint-IntermediatePoint)@"ChkDist"@(Pawn.GetCollisionRadius() * 0.1f));
}
else
{
NumMovePointFails=0;
}
LastMovePoint = IntermediatePoint;
if(NumMovePointFails >= MaxMovePointFails && MaxMovePointFails >= 0)
{
`AILog("ERROR: Got same move location 5x in a row.. something's wrong! bailing from this move");
Goto('FailedMove');
}
else
{
//debug
`AILog( "NextMove"@IntermediatePoint@`showvar(NumMovePointFails) );
}
}
}
//debug
`AILog( "Reached end of move loop??" );
Goto( 'CheckMove' );
FailedMove:
`AILog( "Failed move. Now ZeroMovementVariables" );
MoveTo(Pawn.Location);
Pawn.ZeroMovementVariables();
GotoState('DelayFailure');
ReachedGoal:
//debug
`AILog("Reached move point:"@BP2Vect(NavigationHandle.FinalDestination)@VSize(Pawn.Location-BP2Vect(NavigationHandle.FinalDestination)));
Status = 'Success';
PopCommand( self );
}
/**
* this state will follow our breadcrumbs backward until we are back in the mesh, and then transition back to moving, or go to other fallback states
* if we run out of breadcrumbs and are not yet back in the mesh
*/
state Fallback_Breadcrumbs `DEBUGSTATE
{
function bool ShouldUpdateBreadCrumbs()
{
return false;
}
function bool HandlePathObstruction(Actor BlockedBy)
{
Pawn.SetLocation(IntermediatePoint);
MoveTimer=-1;
GotoState('Fallback_Breadcrumbs','Begin');
return true;
}
Begin:
`AILog("trying to move back along breadcrumb path");
if( NavigationHandle.GetNextBreadCrumb(IntermediatePoint) )
{
`AILog("Moving to breadcrumb pos:"$IntermediatePoint);
// GameAIOwner.DrawDebugLine(Pawn.Location,IntermediatePoint,255,0,0,TRUE);
// GameAIOwner.DrawDebugLine(IntermediatePoint+vect(0,0,100),IntermediatePoint,255,0,0,TRUE);
MoveToDirectNonPathPos(IntermediatePoint);
if( !NavigationHandle.IsAnchorInescapable() )
{
GotoState('Moving');
}
Sleep(0.1);
Goto('Begin');
}
else if( !NavigationHandle.IsAnchorInescapable())
{
GotoState('Moving','Begin');
}
else
{
GotoState('Fallback_FindNearbyMeshPoint');
}
}
state Fallback_FindNearbyMeshPoint `DEBUGSTATE
{
function bool FindAPointWhereICanHoverTo( out Vector out_FallbackDest, float Inradius, optional float minRadius=0, optional float entityRadius = 32, optional bool bDirectOnly=true, optional int MaxPoints=-1,optional float ValidHitBoxSize)
{
local Vector Retval;
local array<vector> poses;
// local int i;
local vector extent;
local vector validhitbox;
Extent.X = entityRadius;
Extent.Y = entityRadius;
Extent.Z = entityRadius;
validhitbox = vect(1,1,1) * ValidHitBoxSize;
NavigationHandle.GetValidPositionsForBox(Pawn.Location,Inradius,Extent,bDirectOnly,poses,MaxPoints,minRadius,validhitbox);
// for(i=0;i<Poses.length;++i)
// {
// DrawDebugStar(poses[i],55.f,255,255,0,TRUE);
// if(i < poses.length-1 )
// {
// DrawDebugLine(poses[i],poses[i+1],255,255,0,TRUE);
// }
// }
if( poses.length > 0)
{
Retval = Poses[Rand(Poses.Length)];
// if for some reason we have a 0,0,0 vect that is never going to be correct
if( VSize(Retval) == 0.0f )
{
out_FallbackDest = vect(0,0,0);
return FALSE;
}
`AILog( `showvar(Retval) );
out_FallbackDest = Retval;
return TRUE;
}
out_FallbackDest = vect(0,0,0);
return FALSE;
}
function bool ShouldUpdateBreadCrumbs()
{
return false;
}
Begin:
`AILog( "Fallback! We now try MoveTo directly to a point that is avail to us" );
if( !FindAPointWhereICanHoverTo( FallbackDest, 2048 ) )
{
GotoState('MoveDown');
}
else
{
MoveToDirectNonPathPos( FallbackDest,, SubGoalReachDist );
Sleep( 0.5f );
if( bFallbackMoveToMesh )
{
GotoState('DelaySuccess');
}
else
{
GotoState('Moving','Begin');
}
}
}
/** Allows subclasses to determine if our enemy is based on an interp actor or not **/
function bool IsEnemyBasedOnInterpActor( Pawn InEnemy )
{
return FALSE;
}
event DrawDebug( HUD H, Name Category )
{
Super.DrawDebug( H, Category );
if(Category != 'Pathing')
{
return;
}
// BLUE
DrawDebugLine( Pawn.Location, GetDestinationPosition(), 0, 0, 255 );
// GREEN
DrawDebugLine( Pawn.Location, BP2Vect(NavigationHandle.FinalDestination), 0, 255, 0 );
NavigationHandle.DrawPathCache( vect(0,0,15) );
}
defaultproperties
{
SubGoalReachDist=128.0
MaxMovePointFails=5
}