284 lines
6.9 KiB
Ucode
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
|
|
} |