1
0
KF2-Dev-Scripts/Engine/Classes/AIController.uc

359 lines
9.2 KiB
Ucode
Raw Permalink Normal View History

2020-12-13 15:01:13 +00:00
//=============================================================================
// AIController, the base class of AI.
//
// Controllers are non-physical actors that can be attached to a pawn to control
// its actions. AIControllers implement the artificial intelligence for the pawns they control.
//
//Copyright 1998-2013 Epic Games, Inc. All Rights Reserved.
//=============================================================================
class AIController extends Controller
native(AI);
`if(`notdefined(__TW_))
/** auto-adjust around corners, with no hitwall notification for controller or pawn
if wall is hit during a MoveTo() or MoveToward() latent execution. */
var bool bAdjustFromWalls;
`endif
/** skill, scaled by game difficulty (add difficulty to this value) */
var float Skill;
/** Move target from last scripted action */
var Actor ScriptedMoveTarget;
/** Route from last scripted action; if valid, sets ScriptedMoveTarget with the points along the route */
var Route ScriptedRoute;
/** if true, we're following the scripted route in reverse */
var bool bReverseScriptedRoute;
/** if ScriptedRoute is valid, the index of the current point we're moving to */
var int ScriptedRouteIndex;
/** view focus from last scripted action */
var Actor ScriptedFocus;
cpptext
{
INT AcceptNearbyPath(AActor *goal);
#if __TW_PATHFINDING_
virtual void AdjustFromWall(FVector HitNormal, AActor* HitActor);
#else
void AdjustFromWall(FVector HitNormal, AActor* HitActor);
#endif
virtual void SetAdjustLocation(FVector NewLoc,UBOOL bAdjust,UBOOL bOffsetFromBase=FALSE);
virtual FVector DesiredDirection();
/** Called when the AIController is destroyed via script */
virtual void PostScriptDestroyed();
}
event PreBeginPlay()
{
Super.PreBeginPlay();
if ( bDeleteMe )
return;
if ( WorldInfo.Game != None )
Skill += WorldInfo.Game.GetModifiedGameDifficulty();
Skill = FClamp(Skill, 0, 3);
}
`if(`__TW_PATHFINDING_)
// Adding declaration here for easier ability to call this event from native code in Engine
event AILog_Internal( coerce string LogText, optional Name LogCategory, optional bool bForce, optional bool BugIt, optional bool bSkipExtraInfo );
`endif
/* Reset()
reset actor to initial state - used when restarting level without reloading.
*/
function Reset()
{
Super.Reset();
}
/**
* list important AIController variables on canvas. HUD will call DisplayDebug() on the current ViewTarget when
* the ShowDebug exec is used
*
* @param HUD - HUD with canvas to draw on
* @input out_YL - Height of the current font
* @input out_YPos - Y position on Canvas. out_YPos += out_YL, gives position to draw text for next debug line.
*/
simulated function DisplayDebug(HUD HUD, out float out_YL, out float out_YPos)
{
local int i;
local string T;
local Canvas Canvas;
Canvas = HUD.Canvas;
super.DisplayDebug(HUD, out_YL, out_YPos);
if (HUD.ShouldDisplayDebug('AI'))
{
Canvas.DrawColor.B = 255;
if ( (Pawn != None) && (MoveTarget != None) && Pawn.ReachedDestination(MoveTarget) )
Canvas.DrawText(" Skill "$Skill$" NAVIGATION MoveTarget "$GetItemName(String(MoveTarget))$"(REACHED) MoveTimer "$MoveTimer, false);
else
Canvas.DrawText(" Skill "$Skill$" NAVIGATION MoveTarget "$GetItemName(String(MoveTarget))$" MoveTimer "$MoveTimer, false);
out_YPos += out_YL;
Canvas.SetPos(4,out_YPos);
Canvas.DrawText(" Destination "$GetDestinationPosition()$" Focus "$GetItemName(string(Focus))$" Preparing Move "$bPreparingMove, false);
out_YPos += out_YL;
Canvas.SetPos(4,out_YPos);
Canvas.DrawText(" RouteGoal "$GetItemName(string(RouteGoal))$" RouteDist "$RouteDist, false);
out_YPos += out_YL;
Canvas.SetPos(4,out_YPos);
for ( i=0; i<RouteCache.Length; i++ )
{
if ( RouteCache[i] == None )
{
if ( i > 5 )
T = T$"--"$GetItemName(string(RouteCache[i-1]));
break;
}
else if ( i < 5 )
T = T$GetItemName(string(RouteCache[i]))$"-";
}
Canvas.DrawText(" RouteCache: "$T, false);
out_YPos += out_YL;
Canvas.SetPos(4,out_YPos);
}
}
event SetTeam(int inTeamIdx)
{
WorldInfo.Game.ChangeTeam(self,inTeamIdx,true);
}
simulated event GetPlayerViewPoint(out vector out_Location, out Rotator out_Rotation)
{
// AI does things from the Pawn
if (Pawn != None)
{
out_Location = Pawn.Location;
out_Rotation = Pawn.Rotation;
}
else
{
Super.GetPlayerViewPoint(out_Location, out_Rotation);
}
}
/**
* Scripting hook to move this AI to a specific actor.
*/
function OnAIMoveToActor(SeqAct_AIMoveToActor Action)
{
local Actor DestActor;
local SeqVar_Object ObjVar;
// abort any previous latent moves
ClearLatentAction(class'SeqAct_AIMoveToActor',true,Action);
// pick a destination
DestActor = Action.PickDestination(Pawn);
// if we found a valid destination
if (DestActor != None)
{
// set the target and push our movement state
ScriptedRoute = Route(DestActor);
if (ScriptedRoute != None)
{
if (ScriptedRoute.RouteList.length == 0)
{
`warn("Invalid route with empty MoveList for scripted move");
}
else
{
ScriptedRouteIndex = 0;
if (!IsInState('ScriptedRouteMove'))
{
PushState('ScriptedRouteMove');
}
}
}
else
{
ScriptedMoveTarget = DestActor;
if (!IsInState('ScriptedMove'))
{
PushState('ScriptedMove');
}
}
// set AI focus, if one was specified
ScriptedFocus = None;
foreach Action.LinkedVariables(class'SeqVar_Object', ObjVar, "Look At")
{
ScriptedFocus = Actor(ObjVar.GetObjectValue());
if (ScriptedFocus != None)
{
break;
}
}
}
else
{
`warn("Invalid destination for scripted move");
}
}
/**
* Simple scripted movement state, attempts to pathfind to ScriptedMoveTarget and
* returns execution to previous state upon either success/failure.
*/
state ScriptedMove
{
event PoppedState()
{
if (ScriptedRoute == None)
{
// if we still have the move target, then finish the latent move
// otherwise consider it aborted
ClearLatentAction(class'SeqAct_AIMoveToActor', (ScriptedMoveTarget == None));
}
// and clear the scripted move target
ScriptedMoveTarget = None;
}
event PushedState()
{
if (Pawn != None)
{
// make sure the pawn physics are initialized
Pawn.SetMovementPhysics();
}
}
Begin:
// while we have a valid pawn and move target, and
// we haven't reached the target yet
while (Pawn != None &&
ScriptedMoveTarget != None &&
!Pawn.ReachedDestination(ScriptedMoveTarget))
{
// check to see if it is directly reachable
if (ActorReachable(ScriptedMoveTarget))
{
// then move directly to the actor
MoveToward(ScriptedMoveTarget, ScriptedFocus);
}
else
{
// attempt to find a path to the target
MoveTarget = FindPathToward(ScriptedMoveTarget);
if (MoveTarget != None)
{
// move to the first node on the path
MoveToward(MoveTarget, ScriptedFocus);
}
else
{
// abort the move
`warn("Failed to find path to"@ScriptedMoveTarget);
ScriptedMoveTarget = None;
}
}
}
// return to the previous state
PopState();
}
/** scripted route movement state, pushes ScriptedMove for each point along the route */
state ScriptedRouteMove
{
event PoppedState()
{
// if we still have the move target, then finish the latent move
// otherwise consider it aborted
ClearLatentAction(class'SeqAct_AIMoveToActor', (ScriptedRoute == None));
ScriptedRoute = None;
}
Begin:
while (Pawn != None && ScriptedRoute != None && ScriptedRouteIndex < ScriptedRoute.RouteList.length && ScriptedRouteIndex >= 0)
{
ScriptedMoveTarget = ScriptedRoute.RouteList[ScriptedRouteIndex].Actor;
if (ScriptedMoveTarget != None)
{
PushState('ScriptedMove');
}
if (Pawn != None && Pawn.ReachedDestination(ScriptedRoute.RouteList[ScriptedRouteIndex].Actor))
{
if (bReverseScriptedRoute)
{
ScriptedRouteIndex--;
}
else
{
ScriptedRouteIndex++;
}
}
else
{
`warn("Aborting scripted route");
ScriptedRoute = None;
PopState();
}
}
if (Pawn != None && ScriptedRoute != None && ScriptedRoute.RouteList.length > 0)
{
switch (ScriptedRoute.RouteType)
{
case ERT_Linear:
PopState();
break;
case ERT_Loop:
bReverseScriptedRoute = !bReverseScriptedRoute;
// advance index by one to get back into valid range
if (bReverseScriptedRoute)
{
ScriptedRouteIndex--;
}
else
{
ScriptedRouteIndex++;
}
Goto('Begin');
break;
case ERT_Circle:
ScriptedRouteIndex = 0;
Goto('Begin');
break;
default:
`warn("Unknown route type");
ScriptedRoute = None;
PopState();
break;
}
}
else
{
ScriptedRoute = None;
PopState();
}
// should never get here
`warn("Reached end of state execution");
ScriptedRoute = None;
PopState();
}
function NotifyWeaponFired(Weapon W, byte FireMode);
function NotifyWeaponFinishedFiring(Weapon W, byte FireMode);
function bool CanFireWeapon( Weapon Wpn, byte FireModeNum ) { return TRUE; }
defaultproperties
{
bAdjustFromWalls=true
bCanDoSpecial=true
MinHitWall=-0.5f
}