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

968 lines
25 KiB
Ucode

//=============================================================================
// AICommand_Debug
//=============================================================================
// Empty (debug) AI command
//=============================================================================
// Killing Floor 2
// Copyright (C) 2015 Tripwire Interactive LLC
//=============================================================================
class AICommand_Debug extends AICommand
within KFAIController;
var bool bTestStepAside;
var bool bNoFocus;
var bool bOldDebugPostRenderInfo;
var vector DebugMoveLocation;
var bool bTempDisableHitWall;
var bool bStoppedRotationTowardEnemy;
var KFPawn DebuggingPlayer;
var float MaxRoamDist;
var float RoamStartTime;
var float RoamEnvelopeOuter;
var float RoamEnvelopeInner;
var float RoamWaitMin;
var float RoamWaitMax;
var actor LastDebugGoal;
//var array<KFJumpSpot> VisitedJumpSpots;
/*********************************************************************************************
* Initialization
**********************************************************************************************/
static function bool Debug( KFAIController AI )
{
local AICommand_Debug Cmd;
if( AI != None )
{
Cmd = new(AI) Default.Class;
if( Cmd != None )
{
AI.PushCommand(Cmd);
return true;
}
}
return false;
}
function Pushed()
{
`AILog( "Entering debug mode", 'Command_Debug' );
bHasDebugCommand = true;
//Focus = none;
ClearTimer( nameof(Debug_CheckRecentMoveTime), Outer );
//SetFocalPoint( Pawn.Location + vector( Pawn.Rotation ) * 512.f );
// if( Enemy != none && Enemy.IsHumanControlled() )
// Enemy = none;
bOldDebugPostRenderInfo = bDebug_PostRenderInfo;
// bDebug_PostRenderInfo = true;
// SetPostRendering( true );
GotoState( 'Wait' );
}
function Resumed( Name OldCommandName )
{
DisableMeleeRangeEventProbing();
// if( OldCommandName == 'AICommand_StepAside' )
// {
// bNoFocus = false;
// }
if( OldCommandName == 'AICommand_MoveToGoal' )
{
`AILog( "Resuming "$self$" after "$OldCommandName$" ChildStatus: "$ChildStatus$" LastDebugGoal: "$LastDebugGoal );
}
if( OldCommandName == 'AICommand_MoveToGoal' && (ChildStatus == 'Aborted' || ChildStatus == 'Failure') && Pawn != none && Pawn.IsAliveAndWell() )
{
`AILog( "Resuming from failed MoveToGoal command", 'PathWarning' );
if( LastDebugGoal != none )
{
Pawn.ZeroMovementVariables();
StopLatentExecution();
msg( "Debug NPC ["$Pawn$"] retrying path to "$LastDebugGoal$", "$VSize(LastDebugGoal.Location - Pawn.Location)$" units away." );
// SetMoveGoal( LastDebugGoal, none, false, 0.f, false, true, true );
}
}
super.Resumed( OldCommandName );
}
function GrabTestTimer()
{
if( VSizeSq( Enemy.Location - Pawn.Location ) < 48400.f ) // 220UU
{
bAllowedToAttack = true;
DoGrabAttack( Enemy );
bAllowedToAttack = false;
}
}
function Popped()
{
bDebug_PostRenderInfo = bOldDebugPostRenderInfo;
SetPostRendering( bDebug_PostRenderInfo );
super.Popped();
bHasDebugCommand = false;
`AILog( "Exiting Debug Mode", 'Command_Debug' );
}
function bool NotifyPlayerBecameVisible( Pawn VisiblePlayer )
{
if( CachedChildCommand != None )
{
`AILog( GetFuncName()$"() Seen: "$VisiblePlayer$" notifying "$CachedChildCommand$" and letting it handle the event.", 'Command_Debug' );
return CachedChildCommand.NotifyPlayerBecameVisible( VisiblePlayer );
}
`AILog( GetFuncName()$" "$VisiblePlayer$" ignoring this event", 'Command_Debug' );
return true;
}
function Debug_Charge();
function Debug_MoveForward();
function Debug_Vision( Pawn P );
function Debug_Flank();
function Debug_Hide();
function Debug_Enemy( Pawn P );
function Debug_StepAside( optional bool bOn = true );
function Debug_Steering();
function Debug_Wander( optional float WanderDuraion=-1.f );
function Debug_DebugLines();
function Debug_Leap();
function Debug_DebugNodes();
function Debug_CrawlerAttack();
function Debug_LOS();
/*********************************************************************************************
* Debugging State (abstract base state)
********************************************************************************************* */
state Debugging
{
event BeginState( name PreviousStateName )
{
`AILog( GetFuncName()$"() in "$GetStateName()$" Previous: "$PreviousStateName, 'Command_Debug' );
}
function InitState()
{
`AILog( GetFuncName()$"() in "$GetStateName(), 'Command_Debug' );
// Focus = none;
// SetFocalPoint( vector(Pawn.Rotation) * 300 );
// DisableSeePlayer();
AIActionStatus = "Awaiting debug command";
}
event EndState( name NextStateName )
{
`AILog( GetFuncName()$"() in "$GetStateName()$" Next: "$NextStateName, 'Command_Debug' );
}
function PushedState()
{
`AILog( GetFuncName()$"() in "$GetStateName(), 'Command_Debug' );
}
function PausedState()
{
`AILog( GetFuncName()$"() in "$GetStateName(), 'Command_Debug' );
}
function ContinuedState()
{
`AILog( GetFuncName()$"() in "$GetStateName(), 'Command_Debug' );
}
function PoppedState()
{
`AILog( GetFuncName()$"() in "$GetStateName(), 'Command_Debug' );
}
function Debug_CrawlerAttack()
{
local PlayerController P;
foreach WorldInfo.AllActors( class'PlayerController', P )
{
if( P.Pawn != None )
{
Enemy = P.Pawn;
}
}
if( Enemy != none )
{
bAllowedToAttack = true;
}
EnableMeleeRangeEventProbing();
Pawn.SetPhysics( PHYS_Walking );
ReadyToMelee();
}
}
state MoveToRandomDebugNodes
{
function Debug_DebugNodes()
{
StopAllLatentMovement();
PopState();
}
function GetGoal()
{
// local KFPathnode KFPN;
// local array<KFPathNode> NavGoals;
// foreach allactors( class'KFPathnode', KFPN )
// {
// if( KFPN.DebugTag != '' && VSize(KFPN.Location - Pawn.Location) > 750.f )
// {
//// NavGoals.AddItem(KFPN);
// }
// }
// SetMoveGoal( NavGoals[ Rand(NavGoals.Length) ] );
}
Begin:
WaitForLanding();
GetGoal();
Sleep( RandRange(3,8) );
Goto( 'Begin' );
}
/*********************************************************************************************
* DebugLOS state
**********************************************************************************************/
state DebugLOS
{
function BeginState( name PreviousStateName )
{
StartLOSTimer();
}
function PushedState()
{
StartLOSTimer();
}
function StartLOSTimer()
{
Disable( 'SeePlayer' );
Disable( 'HearNoise' );
SetTimer( 0.5f, true, nameof(CheckLOS), self );
}
function EndState( name NextStateName )
{
ClearTimer( nameof(CheckLOS), self );
}
function PoppedState()
{
ClearTimer( nameof(CheckLOS), self );
}
function CheckLOS()
{
local KFPlayerController KFPC;
foreach WorldInfo.AllControllers( class'KFPlayerController', KFPC )
{
if( KFPC.Pawn != none && CanSee( KFPC.Pawn ) && Pawn.LineOfSightTo( KFPC.Pawn ) )
{
msg( self$" CAN SEE "$KFPC.Pawn );
}
else
{
msg( self$" CANNOT SEE "$KFPC.Pawn );
}
}
}
}
/*********************************************************************************************
* Wait state (auto state)
********************************************************************************************* */
auto state Wait extends Debugging
{
function BeginState( name PreviousStateName )
{
super.BeginState( PreviousStateName );
//Steering.EnableDefaultAcceleration();
InitState();
}
function ContinuedState()
{
super.ContinuedState();
InitState();
}
/** Called from cheat manager */
function Debug_MoveForward()
{
PushState( 'DebugMoveForward', 'Ready' );
}
/** Called from cheat manager */
function Debug_Hide()
{
PushState( 'DebugHiding' );
}
/** Called from cheat manager */
function Debug_Vision( Pawn P )
{
DebuggingPlayer = KFPawn(P);
PushState( 'DebugVision' );
}
/** Called from cheat manager */
function Debug_Enemy( Pawn P )
{
DebuggingPlayer = KFPawn(P);
PushState( 'DebugEnemy' );
}
function Debug_Leap()
{
PushState( 'DebugLeap' );
}
function Debug_LOS()
{
PushState( 'DebugLOS' );
}
function Debug_DebugNodes()
{
PushState( 'MoveToRandomDebugNodes' );
}
/** Called from cheat manager */
function Debug_StepAside( optional bool bOn = true )
{
if( !bOn )
{
msg( Outer$" : StepAside debugging is already turned off." );
}
else
{
PushState( 'DebugStepAside' );
}
}
/** Called from cheat manager */
function Debug_Steering()
{
PushState( 'DebugSteering' );
}
/** Called from cheat manager */
function Debug_Wander( optional float WanderDuraion=-1.f )
{
class'AICommand_Wander'.static.BeginWander( outer, WanderDuraion, GetALocalPlayerController().Pawn, false );
}
/** Called from cheat manager */
function Debug_DebugLines()
{
PushState( 'DebugDebugLines' );
}
}
// DebugTurn state
state DebugTurn
{
event HandleAICommandSpecialAction()
{
if( GetFocalPoint() != vect(0,0,0) )
{
DrawDebugLine( Pawn.Location, Pawn.Location + normal( GetFocalPoint() - Pawn.Location ) * 1024.f, 0, 255, 0, false );
}
}
function PushedState()
{
SetFocalPoint(vect(0,0,0) );
Focus = none;
}
}
/*********************************************************************************************
* DebugLeap state
**********************************************************************************************/
state DebugLeap
{
function PushedState()
{
local KFWallPathNode KFWP, BestKFWP;
local float BestDist;
foreach WorldInfo.AllNavigationPoints( class'KFWallPathNode', KFWP )
{
if( BestKFWP == None || VSize(KFWP.Location - Pawn.Location) < BestDist )
{
if( KFWP.Location.Z > 350.f )
{
BestDist = VSize(KFWP.Location - Pawn.Location);
BestKFWP = KFWP;
}
}
}
MoveTarget = BestKFWP;
// OldFloor = vect(0,0,1);
//GetAxes(Pawn.Rotation,ViewX,ViewY,ViewZ);
//GroundPitch = 0;
Pawn.bCrawler = true;
}
function bool NotifyHitWall( vector HitNormal, actor Wall )
{
Pawn.SetPhysics( PHYS_Spider );
Pawn.SetBase( Wall, HitNormal );
return false;
}
// function DoWallLeap()
// {
// //LeapTarget = BestKFWP;
//
// OldFloor = vect(0,0,1);
// GetAxes(Pawn.Rotation,ViewX,ViewY,ViewZ);
// //GroundPitch = 0;
// Pawn.bCrawler = true;
// //KFAIController_ZedCrawler(Outer).DoWallLeap( BestKFWP );
// //Pawn.SetCollisionSize(Pawn.Default.CollisionHeight,Pawn.Default.CollisionHeight);
// }
Begin:
if(Pawn.Physics == PHYS_Falling )
{
WaitForLanding();
}
Pawn.SetPhysics(PHYS_Walking);
PushState( 'RotateToMoveTarget' );
Focus = none;
SetFocalPoint(vect(0,0,0));
Sleep( 1.f );
//KFAIController_ZedCrawler(Outer).DoWallLeap( MoveTarget );
Sleep( 0.1f );
if( Pawn.Physics == PHYS_Falling )
{
WaitForLanding();
}
}
/*********************************************************************************************
* DebugSteering state - draws separation steering forces, etc.
********************************************************************************************* */
state DebugSteering extends Debugging
{
function PushedState()
{
super.PushedState();
AIActionStatus = "In DebugSteering state";
bDebug_DrawSeparationSteering = true;
}
function Debug_Steering()
{
PopState();
}
function PoppedState()
{
bDebug_DrawSeparationSteering = false;
super.PoppedState();
}
}
/*********************************************************************************************
* DebugStepAside state - NPC will step aside when bumped or on AIDebugStepSide command
********************************************************************************************* */
state DebugStepAside extends Debugging
{
function PushedState()
{
super.PushedState();
AIActionStatus = "In DebugStepAside state";
msg( Outer$" in DebugStepAside state. Forcing NPC collision will invoke StepAside command, or type AIDEBUGSTEPASIDE again to force NPC to step aside with no collision." );
bIgnoreStepAside = false;
}
function Debug_StepAside( optional bool bOn = true )
{
if( !bOn )
{
msg( Outer$" : Turning off StepAside debugging" );
PopState();
}
else if( CachedChildCommand == none )
{
msg( Outer$" : random step aside" );
}
}
function bool NotifyBump( Actor Other, vector HitNormal )
{
local bool bPrevIgnoreStepAside;
local bool bResult;
`AILog( GetFuncName()$" bumped into "$Other, 'Command_Debug' );
if( Pawn(Other) != none )
{
`AILog( GetFuncName()$" bumped into "$Other$", starting StepAside command", 'Command_Debug' );
bPrevIgnoreStepAside = Outer.bIgnoreStepAside;
Outer.bIgnoreStepAside = false;
StepAsideFor( Pawn(Other),HitNormal );
Outer.bIgnoreStepAside = bPrevIgnoreStepAside;
bResult = true;
}
DisableBump( 1.f );
return bResult;
}
}
/*********************************************************************************************
* DebugJumpPoints state - move to and attempt to use any KFJumpPoints in the level
********************************************************************************************* */
//state DebugJumpPoints extends Debugging
//{
// function PushedState()
// {
// super.PushedState();
// AIActionStatus = "In DebugJumpPoints state";
// DisableSeePlayer();
// }
// function KFPathnode PickJumpDest()
// {
// local KFJumpSpot KFJS;
// local KFJumpSpot ClosestKFJS;
// local float BestDist;
// foreach WorldInfo.AllNavigationPoints( class'KFJumpSpot', KFJS )
// {
// if( !KFJS.bDebug_Visited )
// {
// if( BestDist == 0 || VSize(KFJS.Location - Pawn.Location) < BestDist )
// {
// BestDist = VSize(KFJS.Location - Pawn.Location);
// ClosestKFJS = KFJS;
// }
// }
// }
// return KFPathnode(ClosestKFJS.JumpTarget);
// }
//Begin:
// if( Pawn.Physics == PHYS_Falling )
// {
// WaitForLanding();
// }
// Pawn.SetPhysics( PHYS_Walking );
// SetMoveGoal( PickJumpDest(), none, false, 0, false, true, true, false, false );
// if( ChildStatus == 'Failure' )
// {
// PopState();
// }
// PopState();
//}
/*********************************************************************************************
* DebugEnemy state - Testing NPC threat evaluations and enemy changes
********************************************************************************************* */
function EvaluateThreats();
state DebugEnemy extends Debugging
{
function PushedState()
{
super.PushedState();
EnableSeePlayer();
AIActionStatus = "In DebugEnemy state";
SetTimer( 1.f, true, nameof(EvaluateThreats), self );
}
function EvaluateThreats()
{
local KFPlayerController KFPC;
foreach WorldInfo.AllControllers( class'KFPlayerController', KFPC )
{
EvaluateThreatFrom( KFPC.Pawn );
}
}
// function bool NotifyPlayerBecameVisible( Pawn VisiblePlayer )
// {
// if( Enemy == none )
// {
// ChangeEnemy(VisiblePlayer);
// Focus = Enemy;
// PushState('RotateToFocus');
// return true;
// }
//
// if( Enemy == VisiblePlayer )
// {
// return false;
// }
//
// if( EvaluateThreatFrom(Enemy) < EvaluateThreatFrom(VisiblePlayer) )
// {
// msg( "CHANGING TO "$VisiblePlayer );
// ChangeEnemy(VisiblePlayer);
// Focus = Enemy;
// PushState('RotateToFocus');
// }
//
// return false;
// }
simulated function DrawDebug( HUD HUD, name Category )
{
if( DebuggingPlayer != none )
{
DrawDebugText( HUD, "LOS: "$LineOfSightTo(DebuggingPlayer)$" CanSee: "$CanSee(DebuggingPlayer) );
DrawDebugText( HUD, "VISIBLE BY TRACE: "$IsPawnVisibleViaTrace(DebuggingPlayer) );
}
}
}
/*********************************************************************************************
* DebugHiding state - Hide from player who issued command!
********************************************************************************************* */
state DebugHiding extends Debugging
{
function PushedState()
{
super.PushedState();
MyKFPawn.SetSprinting(true);
AIActionStatus = "In DebugHiding state";
if( Enemy == none )
{
Enemy = GetClosestEnemy();
}
}
function Debug_Hide()
{
PopState();
}
function bool CheckRetreat()
{
local Pawn ClosestEnemy;
ClosestEnemy = GetClosestEnemy();
if( ClosestEnemy != none && CanSee( ClosestEnemy ) )
{
class'Path_AlongLine'.static.AlongLine(Pawn, -Normal(ClosestEnemy.Location - Pawn.Location));
class'Goal_AwayFromPosition'.static.FleeFrom(Pawn, ClosestEnemy.Location, 4000);
if( FindPathToward(ClosestEnemy) != None )
{
AIActionStatus = "Hiding from "$ClosestEnemy$" at "$RouteGoal;
// SetMoveGoal( RouteGoal,, false,128.f, true, true );
return true;
}
}
return false;
}
Begin:
if( Pawn.Physics == PHYS_Falling )
{
WaitForLanding();
}
if( CheckRetreat() )
{
Sleep( 0.25f );
Goto( 'Begin' );
}
MyKFPawn.SetSprinting(false);
PopState();
}
function CheckVision();
/*********************************************************************************************
* DebugVision state (renders pawn view cone, etc.)
********************************************************************************************* */
state DebugVision extends Debugging
{
function PushedState()
{
super.PushedState();
msg( "Debugging "$MyKFPawn$" vision, current PeripheralVision: "$MyKFPawn.PeripheralVision$" Current SightRadius: "$MyKFPawn.SightRadius$" Checking LOS every 0.5 seconds" );
SetTimer( 2.f, true, nameof(CheckVision), self );
AIActionStatus = "In DebugVision state";
EnableSeePlayer();
}
function bool NotifyEnemyBecameVisible( Pawn VisiblePlayer )
{
msg( "YOU ARE VISIBLE : "$normal( Enemy.Location - Pawn.Location ) dot vector(Pawn.Rotation) );
Outer.Enable( 'EnemyNotVisible' );
Outer.Disable( 'SeePlayer' );
return true;
}
function bool NotifyEnemyNotVisible()
{
msg( "YOU ARE NO LONGER VISIBLE"$normal( Enemy.Location - Pawn.Location ) dot vector(Pawn.Rotation) );
Outer.Enable( 'SeePlayer' );
Outer.Disable( 'EnemyNotVisible' );
return true;
}
function CheckVision()
{
}
/** Called from cheat manager */
function Debug_Vision( Pawn P )
{
ClearTimer( nameof(CheckVision) );
PopState();
}
}
function bool NotifyHitWall( vector HitNormal, actor Wall )
{
if( CachedChildCommand != None )
{
`AILog( GetFuncName()$"() notifying "$CachedChildCommand$" and letting it handle the event.", 'HitWall' );
return CachedChildCommand.NotifyHitWall( HitNormal, Wall );
}
return false;
// else if( KFDoorActor(Wall) != none )
// {
// if( KFDoorActor(Wall).WeldIntegrity <= 0 && KFDoorMarker(KFDoorActor(Wall).MyMarker) != none && !KFDoorActor(Wall).IsCompletelyOpen() )
// {
// DisableNotifyHitWall(0.25f);
// WaitForDoor( KFDoorActor(Wall) );
// `AILog( "NotifyHitWall() while in MoveToGoal, Wall: "$Wall$" Using door and waiting for it to open", 'Doors' );
// KFDoorActor(Wall).UseDoor(Pawn);
// return false;
// }
// // NOTE: Unless returning true, if the Wall is a closed door, SuggestMovePreparation event will be called on the associated KFDoorMarker
// `AILog( GetFuncName()$"() Wall: "$Wall$" HitNormal: "$HitNormal$" ran into a door!", 'Doors' );
// if( !KFDoorActor(Wall).IsCompletelyOpen() && KFDoorActor(Wall).WeldIntegrity > 0 && (Pawn.Anchor == KFDoorActor(Wall).MyMarker || (DoorEnemy != none && (DoorEnemy == KFDoorActor(Wall) || PendingDoor == KFDoorActor(Wall)))) )
// {
// `AILog( GetFuncName()$"() calling NotifyAttackDoor for "$Wall, 'Doors' );
// NotifyAttackDoor( KFDoorActor(Wall) );
// return false;
// //`AILog( GetFuncName()$"() has door enemy "$DoorEnemy, 'Doors' );
// }
// }
// return false;
}
function EnableNotifyHitWall()
{
bTempDisableHitWall = false;
}
function bool ReadyToCharge()
{
return false;
}
/*********************************************************************************************
* Debug Move Forward - send pawn into latent move forward in direction pawn is facing
********************************************************************************************* */
state DebugMoveForward extends Debugging
{
function PushedState()
{
super.PushedState();
AIActionStatus = "In DebugMoveForward state";
}
Ready:
Steering.EnableDefaultAcceleration();
MoveTo( Pawn.Location + (vector(Pawn.Rotation) * 1024) );
Steering.DisableDefaultAcceleration();
PopState();
}
/*********************************************************************************************
* RotateToFocus state
********************************************************************************************* */
state RotateToFocus extends Debugging
{
function PushedState()
{
super.PushedState();
AIActionStatus = "In RotateToFocus state";
}
Begin:
SetFocalPoint( vect(0,0,0) );
Focus = Enemy;
// Pawn.DesiredRotation = Normalize(Rotator( (Focus == None ? GetFocalPoint() : Focus.Location) - Pawn.Location ));
// DesiredRotation = Pawn.DesiredRotation;
FinishRotation();
SetFocalPoint(Vect(0,0,0));
Pawn.ResetDesiredRotation();
Focus = none;
PopState();
}
state RotateToMoveTarget extends Debugging
{
function PushedState()
{
super.PushedState();
AIActionStatus = "In RotateToFocus state";
}
Begin:
SetFocalPoint( vect(0,0,0) );
Focus = MoveTarget;
// Pawn.DesiredRotation = Normalize(Rotator( (Focus == None ? GetFocalPoint() : Focus.Location) - Pawn.Location ));
// DesiredRotation = Pawn.DesiredRotation;
FinishRotation();
SetFocalPoint(Vect(0,0,0));
Pawn.ResetDesiredRotation();
Focus = none;
PopState();
}
/*********************************************************************************************
* DebugRoaming state - NPC will roam to locations within envelope distance of player
********************************************************************************************* */
state DebugRoaming extends Debugging
{
function PushedState()
{
super.PushedState();
if( Enemy == none )
{
Enemy = GetClosestEnemy();
}
AIActionStatus = "In DebugRoaming state";
RoamStartTime = WorldInfo.TimeSeconds;
}
function Debug_Wander( optional float WanderDuraion=-1.f )
{
PopState();
}
function Actor GenerateRoamPath( Actor Goal, optional float Distance, optional bool bAllowPartialPath )
{
local Actor NewGoal;
class'Path_TowardGoal'.static.TowardGoal( Pawn, Goal );
class'Path_WithinTraversalDist'.static.DontExceedMaxDist( Pawn, MaxRoamDist, false );
// real one with soft distances
class'Path_WithinDistanceEnvelope'.static.StayWithinEnvelopeToLoc( Pawn, Goal.Location, RoamEnvelopeOuter, RoamEnvelopeInner,false,, true );
// don't go somewhere we can't get out of
class'Path_AvoidInEscapableNodes'.static.DontGetStuck( Pawn );
class'Goal_Null'.static.GoUntilBust( Pawn, 1024 );
NewGoal = FindPathToward( Goal );
Pawn.ClearConstraints();
return NewGoal;
}
function bool RoamTowardEnemy()
{
local actor Path;
Path = GenerateRoamPath( Enemy, 100.f, true );
if( Path != none )
{
// SetMoveGoal( RouteGoal,,,,true);
return true;
}
return false;
}
simulated function DrawDebug( HUD HUD, name Category )
{
DrawDebugText( KFHUDBase(HUD), "MaxRoamDist: "$MaxRoamDist$" RoamWaitMin: "$RoamWaitMin$" RoamWaitMax: "$RoamWaitMax );
DrawDebugText( KFHUDBase(HUD), "RoamEnvelopeInner: "$RoamEnvelopeInner$" RoamEnvelopeOuter: "$RoamEnvelopeOuter );
}
Begin:
AIActionStatus = "Roaming toward enemy ("$Enemy$")";
if( Enemy == none || !RoamTowardEnemy() )
{
AIActionStatus = "Failed to find roam location for "$Enemy;
Sleep(1.5f);
Goto('Begin');
}
// if we can't see our enemy from here, roam again!
if( !IsPawnVisibleViaTrace(Enemy) )
{
`AILog( "Enemy wasn't visible, reacquiring", 'Command_Debug' );
Sleep( 0.f );
Goto( 'Begin' );
}
AIActionStatus = "Rotating toward enemy";
Focus = Enemy;
SetFocalPoint( vect(0,0,0) );
Focus = MoveFocus;
FinishRotation();
Sleep( RandRange(RoamWaitMin, RoamWaitMax) );
Goto( 'Begin' );
}
function bool CanSeePawn( Pawn Seen )
{
return true;
}
/*********************************************************************************************
* Debugging
********************************************************************************************* */
simulated function DrawDebug( HUD HUD, name Category )
{
//DrawDebugText( KFHUDBase(HUD), "Testing output from debug command" );
}
defaultproperties
{
bAllowedToAttack=true
bIgnoreNotifies=false
bIgnoreStepAside=false
MaxRoamDist=4096.f
RoamEnvelopeOuter=1800.f
RoamEnvelopeInner=750.f
RoamWaitMin=0.5f
RoamWaitMax=3.5f
}