//=============================================================================
// 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
}
