1
0
KF2-Dev-Scripts/KFGame/Classes/AICommand_Flee.uc
2020-12-13 18:01:13 +03:00

284 lines
6.9 KiB
Ucode

//=============================================================================
// AICommand_Flee
//=============================================================================
// Directs the AI away from a target for a specified duration and distance
//=============================================================================
// Killing Floor 2
// Copyright (C) 2015 Tripwire Interactive LLC
//=============================================================================
class AICommand_Flee extends AICommand
within KFAIController;
/* Tells the command to stop when it reaches the goal */
var bool bStopAtGoal;
/* Actor to flee away from */
var Actor FleeTarget;
/* Dot product of projection between pawn and goal location */
var float Dot;
/** Cached value of the sprinting state before fleeing, to restore upon command pop */
var bool bWasSprinting;
/* How many unsuccessful path attempts have been made since command was pushed */
var int PathAttempts;
/* The maximum duration that the flee command can run */
var float FleeDuration;
/** The desired distance to attempt to flee.
Note that this is not based on direct projection, but visited weight. */
var float FleeDistance;
/** Returns true if we've gotten a goal and want to continue towards it */
var bool bHaveGoal;
/** A randomized distance outside of the target goal to stop at */
var float GoalOffset;
/*********************************************************************************************
* Initialization
********************************************************************************************* */
static function bool FleeFrom(
KFAIController AI,
actor inFleeTarget,
optional float inFleeDuration,
optional float inFleeDistance=5000,
optional bool bShouldStopAtGoal=false )
{
local AICommand_Flee Cmd;
if( AI != None )
{
Cmd = new(AI) Default.Class;
if( Cmd != None )
{
Cmd.FleeTarget = inFleeTarget;
Cmd.FleeDistance = inFleeDistance;
Cmd.FleeDuration = inFleeDuration;
Cmd.bStopAtGoal = bShouldStopAtGoal;
AI.PushCommand(Cmd);
return true;
}
}
return false;
}
function Pushed()
{
super.Pushed();
bWasSprinting = MyKFPawn.bIsSprinting;
DisableMeleeRangeEventProbing();
MoveTimer = -1.0;
Pawn.ZeroMovementVariables();
StopAllLatentMovement();
RouteGoal = None;
bHaveGoal = false;
GoalOffset = Square(384.f + (fRand()*128.f));
if( FleeDuration > 0.f )
{
SetTimer( FleeDuration, false, nameof(Timer_FleeDurationExpired), self );
}
StartFleeing();
}
function Timer_FleeDurationExpired()
{
Status = 'Success';
NotifyFleeFinished();
PopCommand( self );
}
function StartFleeing()
{
GotoState('Fleeing', 'Begin');
}
function Popped()
{
super.Popped();
if( MyKFPawn != none )
{
MyKFPawn.bIsSprinting = bWasSprinting;
MyKFPawn.ClearHeadTrackTarget( FleeTarget );
}
ClearTimer( nameOf(Timer_FleeDurationExpired), self );
EnableMeleeRangeEventProbing();
}
/*********************************************************************************************
* Hiding state
********************************************************************************************* */
state Fleeing
{
event BeginState( name PreviousStateName )
{
super.BeginState( PreviousStateName );
Enemy = None;
}
function bool CheckRetreat()
{
local EPathSearchType OldSearchType;
if( RouteGoal != none && bHaveGoal )
{
AIActionStatus = "Attempting to flee from ["$FleeTarget$"] at ["$RouteGoal$"]";
return true;
}
else
{
bHaveGoal = false;
}
// Make sure we always have something to flee from
if( FleeTarget == none )
{
Class'NavigationPoint'.static.GetNearestNavToActor( Pawn );
}
// Cache off our previous path search type
OldSearchType = Pawn.PathSearchType;
if( FleeTarget != none )
{
AIActionStatus = "Searching for navigable path from ["$FleeTarget$"]";
Pawn.PathSearchType = PST_Constraint;
class'Path_AlongLine'.static.AlongLine( Pawn, Normal(Pawn.Location - FleeTarget.Location) );
class'Goal_AwayFromPosition'.static.FleeFrom( Pawn, FleeTarget.Location, FleeDistance );
if( FindPathToward( Pawn ) != None )
{
//Pawn.DrawDebugLine( RouteGoal.Location, Pawn.Location, 255, 80, 80, true );
//Pawn.DrawDebugSphere( RouteGoal.Location, 128, 4, 255, 80, 80, true );
bHaveGoal = true;
AIActionStatus = "Attempting to flee from ["$FleeTarget$"] at ["$RouteGoal$"]";
Focus = none;
PathAttempts = 0;
// Restore previous path search type
Pawn.PathSearchType = OldSearchType;
return true;
}
}
// Restore previous path search type
Pawn.PathSearchType = OldSearchType;
PathAttempts += 1;
return false;
}
Begin:
// Make sure we have a pawn and it's alive
if( MyKFPawn == none || !MyKFPawn.IsAliveAndWell() )
{
Status = 'Failure';
`AILog( "No pawn, aborting", 'Command_Flee' );
NotifyFleeFinished();
PopCommand( self );
}
else
{
// Don't try to path when falling
if( MyKFPawn.Physics == PHYS_Falling )
{
AIActionStatus = "Fleeing, waiting for landing...";
WaitForLanding();
}
// Make sure our pawn didn't die from falling!
if( MyKFPawn == none || !MyKFPawn.IsAliveAndWell() )
{
Status = 'Failure';
`AILog( "No pawn, or pawn died from falling", 'Command_Flee' );
NotifyFleeFinished();
PopCommand( self );
}
else
{
// Keep focus clear
Focus = None;
if( CheckRetreat() )
{
// Always sprint when fleeing
MyKFPawn.SetSprinting( true );
// Continually move us to our goal
SetMoveGoal( RouteGoal, none, false, 256.f, true, true, true, false, false );
// Stop at goal if we are close enough
if( !bStopAtGoal || VSizeSQ(Pawn.Location - RouteGoal.Location) > GoalOffset )
{
Sleep( 0.f );
Goto( 'Begin' );
}
// Rotate to direction
Dot = Normal(Location - FleeTarget.Location) dot vector(FleeTarget.Rotation);
if( Dot > 0.78f )
{
PushState( 'RotateToFocus' );
Status = 'Success';
Sleep( 0.f );
NotifyFleeFinished();
PopCommand( self );
}
else
{
Status = 'Success';
Sleep( 0.f );
NotifyFleeFinished();
PopCommand( self );
}
}
else if( PathAttempts > 30 )
{
// Don't get stuck, pop the command so the notify triggers
Status = 'Failure';
`AILog( "Failed to find flee path from ["$FleeTarget$"] after 30 attempts, aborting...", 'Command_Flee' );
NotifyFleeFinished();
PopCommand( self );
}
else
{
Sleep( 0.f );
Goto( 'Begin' );
}
}
}
FleeComplete:
Status = 'Success';
NotifyFleeFinished();
PopCommand( self );
}
state RotateToFocus `DEBUGSTATE
{
Begin:
AIActionStatus = "Rotating to focus";
SetFocalPoint( vect(0,0,0) );
Focus = FleeTarget;
FinishRotation();
PopState();
}
DefaultProperties
{
bAllowedToAttack=false
}