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

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
}