234 lines
6.8 KiB
Ucode
234 lines
6.8 KiB
Ucode
//=============================================================================
|
|
// AICommand_Evade
|
|
//=============================================================================
|
|
// Handles starting/stopping evade special move. Can be used for a variety
|
|
// of reasons, but main purpose is to evade away from grenades/projectiles.
|
|
//
|
|
// Use KFAIController.DoEvade() to start this action.
|
|
//
|
|
// Typically, this is what happens:
|
|
// 1) Player's GetAdjustedAim() sets the player's ShotTarget to be the pawn
|
|
// the player is aiming at.
|
|
// 2) Projectile.PreBeginPlay() calls ReceiveProjectileWarning() for the
|
|
// ShotTarget.
|
|
// 3) KFAIController.ReceiveProjectileWarning() starts a brief timer which goes
|
|
// on to start the evade command. It also warns other Zeds within a radius
|
|
// of about 900 units, calling HandleProjectileWarning() for each notified
|
|
// NPC. Conditions are taken into account prior to warning anothehr Zed,
|
|
// like ensuring that they are somewhat facing the instigator's location.
|
|
//
|
|
//=============================================================================
|
|
// Killing Floor 2
|
|
// Copyright (C) 2015 Tripwire Interactive LLC
|
|
//=============================================================================
|
|
|
|
class AICommand_Evade extends AICommand_SpecialMove
|
|
within KFAIController;
|
|
|
|
/** Direction in which to evade */
|
|
var byte EvadeDirection;
|
|
/** Optional delay before starting the evade special move */
|
|
var float EvadeDelay;
|
|
/** SpecialMoveFlags used to determine which animation to play */
|
|
var byte SMFlags;
|
|
/** Determines whether to use a regular evade, or an "evade in fear" (different set of animations) */
|
|
var bool bFrightened;
|
|
/** If true, NPC will turn to face the threat before starting the special moe */
|
|
var bool bTurnToThreat;
|
|
/** There used to be a check when the special move completes, to see if another immediate evade is necessary.
|
|
This code is currently commented out (see SpecialMoveFinished() below). RepeatDistSq was used with this. */
|
|
var float RepeatDistSq;
|
|
/** If TRUE, AI will use the LookAt system to glance at what they're evading */
|
|
var bool bLookAtDangerInstigator;
|
|
/** Location to look at if bLookAtDangerInstigator is TRUE */
|
|
var vector LookAtLocation;
|
|
/** Previous value of MyKFPawn.bIsHeadTrackingActive */
|
|
var bool bOldHeadTrackingActive;
|
|
|
|
/** Simple constructor that pushes a new instance of the command for the AI */
|
|
static function bool Evade( KFAIController AI,
|
|
byte Direction,
|
|
optional float InEvadeDelay,
|
|
optional bool InFrightened,
|
|
optional bool InTurnToThreat,
|
|
optional vector InLookAtLocation )
|
|
{
|
|
local AICommand_Evade Cmd;
|
|
|
|
if( AI != None )
|
|
{
|
|
Cmd = new(AI) class'AICommand_Evade';
|
|
if( Cmd != None )
|
|
{
|
|
Cmd.EvadeDirection = Direction;
|
|
Cmd.EvadeDelay = InEvadeDelay;
|
|
Cmd.SMFlags = Direction;
|
|
Cmd.bFrightened = InFrightened;
|
|
Cmd.bTurnToThreat = InTurnToThreat;
|
|
|
|
if( InLookAtLocation != vect(0,0,0) )
|
|
{
|
|
Cmd.bLookAtDangerInstigator = true;
|
|
Cmd.LookAtLocation = InLookAtLocation;
|
|
}
|
|
|
|
AI.PushCommand( Cmd );
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/** Build debug string */
|
|
event String GetDumpString()
|
|
{
|
|
return super.GetDumpString()@"Dir:"@EvadeDirection;
|
|
}
|
|
|
|
function Pushed()
|
|
{
|
|
AIActionStatus = "Evading!";
|
|
Super.Pushed();
|
|
|
|
AIZeroMovementVariables();
|
|
// Keep pawn's rotation as desired
|
|
SetDesiredRotation( Pawn.Rotation );
|
|
SetFocalPoint( vect(0,0,0) );
|
|
Focus = none;
|
|
/** Optionally pause in the 'Wait' state - use to hit reactions so a cluster of
|
|
Zeds don't all attempt to evade at the same instant */
|
|
if( EvadeDelay > 0.f )
|
|
{
|
|
GotoState( 'Wait' );
|
|
}
|
|
else
|
|
{
|
|
GotoState( 'Command_SpecialMove' );
|
|
}
|
|
}
|
|
|
|
function Popped()
|
|
{
|
|
AIActionStatus = "Done Evading";
|
|
AIZeroMovementVariables();
|
|
if( MyKFPawn != none )
|
|
{
|
|
MyKFPawn.StopLookingAtPawn();
|
|
}
|
|
Focus = none;
|
|
|
|
// Disable head tracking
|
|
if( bLookAtDangerInstigator && MyKFPawn != none && MyKFPawn.bCanHeadTrack)
|
|
{
|
|
MyKFPawn.bIsHeadTrackingActive = bOldHeadTrackingActive;
|
|
MyKFPawn.MyLookAtInfo.ForcedLookAtLocation = vect(0,0,0);
|
|
}
|
|
|
|
Super.Popped();
|
|
}
|
|
|
|
function bool CanEvade()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
state Wait
|
|
{
|
|
/** Look at what we're evading if bLookAtDangerInstigator=TRUE */
|
|
function BeginState( name PreviousStateName )
|
|
{
|
|
if( bLookAtDangerInstigator && MyKFPawn.bCanHeadTrack )
|
|
{
|
|
// Head tracking
|
|
if( MyKFPawn.IK_Look_Head == none )
|
|
{
|
|
MyKFPawn.IK_Look_Head = SkelControlLookAt( MyKFPawn.Mesh.FindSkelControl('HeadLook') );
|
|
}
|
|
bOldHeadTrackingActive = MyKFPawn.bIsHeadTrackingActive;
|
|
MyKFPawn.bIsHeadTrackingActive = true;
|
|
MyKFPawn.MyLookAtInfo.LookAtPct = 1.f;
|
|
MyKFPawn.MyLookAtInfo.BlendOut = 0.33f;
|
|
MyKFPawn.MyLookAtInfo.BlendIn = fMax( EvadeDelay * 0.95f, 0.2f );
|
|
MyKFPawn.MyLookAtInfo.ForcedLookAtLocation = LookAtLocation;
|
|
}
|
|
}
|
|
|
|
Begin:
|
|
Sleep( EvadeDelay );
|
|
GotoState('Command_SpecialMove');
|
|
}
|
|
|
|
state Command_SpecialMove
|
|
{
|
|
/** Determine whether to use a "fear" evade or a regular evade. */
|
|
function ESpecialMove GetSpecialMove()
|
|
{
|
|
/** Not every Zed has an evade-in-fear special move */
|
|
if( bFrightened && MyKFPawn.CanDoSpecialMove(SM_Evade_Fear) )
|
|
{
|
|
return SM_Evade_Fear;
|
|
}
|
|
else
|
|
{
|
|
return SM_Evade;
|
|
}
|
|
}
|
|
|
|
/** Pack the animflags, passing in EvadeDirection to get the appropriate animation sequence */
|
|
function byte GetSpecialMoveFlags( ESpecialMove InSpecialMove )
|
|
{
|
|
return class'KFSM_Evade'.static.PackAnimFlag( EvadeDirection );
|
|
}
|
|
|
|
/** Optionally don't begin the special move until rotation is complete */
|
|
function bool ShouldFinishRotation()
|
|
{
|
|
return bTurnToThreat;
|
|
}
|
|
|
|
/** Begin executing the special move */
|
|
function bool ExecuteSpecialMove()
|
|
{
|
|
SpecialMove = GetSpecialMove();
|
|
|
|
`AILog( GetFuncName()$"()"@SpecialMove, 'Command_SpecialMove' );
|
|
|
|
if( SpecialMove != SM_None && (!bShouldCheckSpecialMove || MyKFPawn.CanDoSpecialMove( SpecialMove )) )
|
|
{
|
|
MyKFPawn.DoSpecialMove(SpecialMove, true, GetInteractionPawn(), GetSpecialMoveFlags(GetSpecialMove()) );
|
|
AIActionStatus = "SpecialMove: "$MyKFPawn.SpecialMoves[SpecialMove];
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
function FinishedSpecialMove()
|
|
{
|
|
// local byte BestDir;
|
|
//
|
|
// if( EvadeFromActor != none && Projectile(EvadeFromActor) != none && VSizeSq(Pawn.Location - EvadeFromActor.Location) <= RepeatDistSq )
|
|
// {
|
|
// BestDir = GetBestEvadeDir( EvadeFromActor.Location, Enemy );
|
|
// EvadeDirection = BestDir;
|
|
// SMFlags = BestDir;
|
|
// TimeOutDelaySeconds = default.TimeOutDelaySeconds;
|
|
// GotoState( GetStateName(), 'Begin', true );
|
|
// }
|
|
// else
|
|
// {
|
|
super.FinishedSpecialMove();
|
|
// }
|
|
}
|
|
}
|
|
|
|
defaultproperties
|
|
{
|
|
bAllowedToAttack=false
|
|
bShouldCheckSpecialMove=true
|
|
RepeatDistSq=250000.f
|
|
}
|