231 lines
5.9 KiB
Ucode
231 lines
5.9 KiB
Ucode
|
//=============================================================================
|
||
|
// AICommand_Wander
|
||
|
//=============================================================================
|
||
|
// Wander AICommand - originally had options to wander within the confines
|
||
|
// of a specified volume, but that part has been stripped out.
|
||
|
//=============================================================================
|
||
|
// Killing Floor 2
|
||
|
// Copyright (C) 2015 Tripwire Interactive LLC
|
||
|
//=============================================================================
|
||
|
class AICommand_Wander extends AICommand
|
||
|
within KFAIController;
|
||
|
|
||
|
var vector WanderDestination;
|
||
|
var float MaxWanderDist;
|
||
|
/** Number of seconds to wander (-1 no time limit) */
|
||
|
var float WanderDuration;
|
||
|
var float RandomCoef;
|
||
|
var float WanderEnvelopeInner;
|
||
|
/** Min/Max delay time after reaching each wander point */
|
||
|
var float WanderWaitMin;
|
||
|
var float WanderWaitMax;
|
||
|
/** Goal to wander toward or to wander around */
|
||
|
var actor WanderGoal;
|
||
|
/** Wanders around the goal, not straying too far */
|
||
|
var bool bWanderAroundGoal;
|
||
|
/** Whether or not partial paths are acceptible while wandering */
|
||
|
var bool bCanUsePartialPath;
|
||
|
/** Cached enemy */
|
||
|
/** Cached bIsSprinting */
|
||
|
var bool bWasSprinting;
|
||
|
|
||
|
static function bool BeginWander( KFAIController AI, optional float InWanderDuration, optional actor InWanderGoal, optional bool inWanderAroundGoal, optional float InMaxWanderDist=10000.f )
|
||
|
{
|
||
|
local AICommand_Wander Cmd;
|
||
|
|
||
|
if( AI != None )
|
||
|
{
|
||
|
Cmd = new(AI) Default.Class;
|
||
|
if( Cmd != None )
|
||
|
{
|
||
|
Cmd.WanderDuration = InWanderDuration;
|
||
|
Cmd.MaxWanderDist = InMaxWanderDist;
|
||
|
Cmd.bWanderAroundGoal = inWanderAroundGoal;
|
||
|
Cmd.WanderGoal = InWanderGoal;
|
||
|
AI.PushCommand(Cmd);
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
function Pushed()
|
||
|
{
|
||
|
Super.Pushed();
|
||
|
bWasSprinting = MyKFPawn.bIsSprinting;
|
||
|
MyKFPawn.bIsSprinting = false;
|
||
|
MyKFPawn.bCanUseHiddenSpeed = false;
|
||
|
|
||
|
if( WanderDuration > 0.f )
|
||
|
{
|
||
|
SetTimer( WanderDuration, false, nameof(Timer_WanderDurationExpired), self );
|
||
|
}
|
||
|
|
||
|
Enemy = none;
|
||
|
GotoState( 'Wandering' );
|
||
|
}
|
||
|
|
||
|
function Paused( GameAICommand NewCommand )
|
||
|
{
|
||
|
super.Paused( NewCommand );
|
||
|
AIZeroMovementVariables( true );
|
||
|
StopAllLatentMovement();
|
||
|
MyKFPawn.bCanUseHiddenSpeed = MyKFPawn.default.bCanUseHiddenSpeed;
|
||
|
}
|
||
|
|
||
|
function Resumed( name OldCommandName )
|
||
|
{
|
||
|
MyKFPawn.bIsSprinting = bWasSprinting;
|
||
|
super.Resumed( OldCommandName );
|
||
|
MyKFPawn.bCanUseHiddenSpeed = false;
|
||
|
}
|
||
|
|
||
|
function Popped()
|
||
|
{
|
||
|
super.Popped();
|
||
|
ClearTimer( nameof(Timer_WanderDurationExpired), self );
|
||
|
AIZeroMovementVariables( true );
|
||
|
StopAllLatentMovement();
|
||
|
|
||
|
if( MyKFPawn != none )
|
||
|
{
|
||
|
MyKFPawn.bCanUseHiddenSpeed = MyKFPawn.default.bCanUseHiddenSpeed;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function Timer_WanderDurationExpired()
|
||
|
{
|
||
|
Status = 'Success';
|
||
|
AIZeroMovementVariables( true );
|
||
|
StopAllLatentMovement();
|
||
|
Status = 'Success';
|
||
|
PopCommand( self );
|
||
|
}
|
||
|
|
||
|
function Vector GetMoveDir()
|
||
|
{
|
||
|
return ( (vector(Pawn.Rotation)/RandomCoef) + (RandomCoef * VRand()) );
|
||
|
}
|
||
|
|
||
|
state Wandering
|
||
|
{
|
||
|
function Actor GenerateWanderPath( Actor Goal, optional bool bAllowPartialPath )
|
||
|
{
|
||
|
local float DistToGoal;
|
||
|
local Actor Ret;
|
||
|
|
||
|
bCanUsePartialPath = bAllowPartialPath;
|
||
|
|
||
|
if( Goal == none )
|
||
|
{
|
||
|
return none;
|
||
|
}
|
||
|
|
||
|
DistToGoal = VSize( Pawn.Location - Goal.Location );
|
||
|
|
||
|
if( bWanderAroundGoal && VSize(Goal.Location - Pawn.Location) < MaxWanderDist )
|
||
|
{
|
||
|
class'Path_AlongLine'.static.AlongLine( Pawn, -Normal(Goal.Location - Pawn.Location));
|
||
|
class'Path_WithinTraversalDist'.static.DontExceedMaxDist( Pawn, MaxWanderDist, false );
|
||
|
class'Goal_Random'.static.FindRandom( Pawn, 1024.f, -1 );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
class'Path_TowardGoal'.static.TowardGoal( Pawn, Goal );
|
||
|
class'Path_WithinTraversalDist'.static.DontExceedMaxDist( Pawn, MaxWanderDist, false );
|
||
|
class'Path_WithinDistanceEnvelope'.static.StayWithinEnvelopeToLoc(Pawn, Goal.Location, MaxWanderDist, Min(DistToGoal,WanderEnvelopeInner),FALSE,,TRUE);
|
||
|
class'Goal_Null'.static.GoUntilBust( Pawn, 1024 );
|
||
|
}
|
||
|
|
||
|
Ret = FindPathToward( Goal,,, bAllowPartialPath );
|
||
|
|
||
|
Pawn.ClearConstraints();
|
||
|
return Ret;
|
||
|
}
|
||
|
|
||
|
function bool Wander()
|
||
|
{
|
||
|
local actor Path;
|
||
|
|
||
|
RouteGoal = none;
|
||
|
Path = none;
|
||
|
|
||
|
Path = GenerateWanderPath( WanderGoal, true );
|
||
|
if( Path != none )
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
Begin:
|
||
|
if( WanderGoal == none )
|
||
|
{
|
||
|
if( SuggestNewWanderPoint(WanderDestination, GetMoveDir(), MaxWanderDist) )
|
||
|
{
|
||
|
SetMovePoint( WanderDestination,, false, 128.f, false );
|
||
|
}
|
||
|
Sleep( 1.f + (2*FRand()) );
|
||
|
Goto( 'Begin' );
|
||
|
}
|
||
|
|
||
|
`AILog("Wandering toward enemy...");
|
||
|
if( !Wander() )
|
||
|
{
|
||
|
`AILog("Failed to find Wander location for "$Enemy);
|
||
|
Sleep( 1.5f );
|
||
|
Goto( 'Begin' );
|
||
|
}
|
||
|
else if( RouteGoal != none )
|
||
|
{
|
||
|
SetMoveGoal( RouteGoal,, false, 128.f, true,,,, bCanUsePartialPath );
|
||
|
Sleep( RandRange( WanderWaitMin, WanderWaitMax ) );
|
||
|
Goto( 'FinishedMove' );
|
||
|
}
|
||
|
Sleep( 0.f );
|
||
|
Goto( 'Begin' );
|
||
|
|
||
|
FinishedMove:
|
||
|
`AILog("Done Wandering");
|
||
|
|
||
|
// if we can't see our enemy from here, Wander again!
|
||
|
// if( !bWanderAwayFromGoal && !IsPawnVisibleViaTrace(Enemy) )
|
||
|
// {
|
||
|
// `AILog("Enemy wasn't visible, reacquiring");
|
||
|
// GotoState('Reacquire');
|
||
|
// }
|
||
|
|
||
|
`AILog("Enemy was visible, rotating toward him");
|
||
|
|
||
|
`AILog("Finished rotating, waiting a bit to let our driver pwn his arse");
|
||
|
Sleep( RandRange(WanderWaitMin, WanderWaitMax) );
|
||
|
Goto( 'Begin' );
|
||
|
}
|
||
|
|
||
|
function bool IsPawnVisibleViaTrace( Pawn PawnToCheck )
|
||
|
{
|
||
|
local Vector TestLocation;
|
||
|
local Rotator Rot;
|
||
|
|
||
|
Rot = Pawn.Rotation;
|
||
|
TestLocation = PawnToCheck.GetPawnViewLocation();
|
||
|
Rot = Rotator(PawnToCheck.Location - Pawn.Location);
|
||
|
|
||
|
return CanSeePawn(PawnToCheck) && CanSeeByPoints( Pawn.Location, TestLocation, Rot );
|
||
|
}
|
||
|
|
||
|
function bool CanSeePawn( Pawn Seen )
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
DefaultProperties
|
||
|
{
|
||
|
MaxWanderDist=10000.f
|
||
|
WanderEnvelopeInner=1500.f
|
||
|
WanderWaitMin=0.25f
|
||
|
WanderWaitMax=1.33f
|
||
|
RandomCoef=1.05f
|
||
|
}
|