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

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
}