341 lines
8.7 KiB
Ucode
341 lines
8.7 KiB
Ucode
/**
|
|
* Copyright 1998-2013 Epic Games, Inc. All Rights Reserved.
|
|
*/
|
|
class GameAIController extends AIController
|
|
dependson(GameTypes)
|
|
abstract
|
|
native
|
|
config(Game);
|
|
|
|
|
|
/** Current command stack, with the last element being the currently active (ticked) one */
|
|
var() editinline transient const GameAICommand CommandList;
|
|
|
|
/** Whether this AI has a runaway loop or not. If it does we are going to do AbortCommand( CommandList ) at the end of Tick(). **/
|
|
var transient bool bHasRunawayCommandList;
|
|
|
|
/** Debug log file, @see: AILog */
|
|
var transient FileLog AILogFile;
|
|
|
|
var(Debug) config bool bAILogging;
|
|
var(Debug) config bool bAILogToWindow;
|
|
var(Debug) config bool bFlushAILogEachLine;
|
|
var(Debug) config bool bMapBasedLogName;
|
|
var(Debug) config bool bAIDrawDebug;
|
|
var transient bool bAIBroken;
|
|
|
|
var(Debug) float DebugTextMaxLen;
|
|
var(Debug) transient const array<AICmdHistoryItem> CommandHistory;
|
|
var(Debug) config int CommandHistoryNum;
|
|
|
|
/** List of categories to filter */
|
|
var(Debug) config array<Name> AILogFilter;
|
|
|
|
// DEMO RECORDING PROPERTIES - for saving AI info into demos
|
|
var string DemoActionString;
|
|
|
|
|
|
cpptext
|
|
{
|
|
#if !DO_AI_LOGGING
|
|
|
|
#if COMPILER_SUPPORTS_NOOP
|
|
#define AIObjLog __noop
|
|
#define AILog __noop
|
|
#define AI_LOG __noop
|
|
#elif SUPPORTS_VARIADIC_MACROS
|
|
#define AIObjLog(...)
|
|
#define AILog(...)
|
|
#define AI_LOG(...)
|
|
#else
|
|
#define AIObjLog GNull->Logf
|
|
#define AILog GNull->Logf
|
|
#define AI_LOG GNull->Logf
|
|
#endif
|
|
|
|
#else
|
|
#define AI_LOG(Object, FuncParams) \
|
|
if(RUNTIME_DO_AI_LOGGING)\
|
|
{\
|
|
Object->AILog FuncParams;\
|
|
}
|
|
|
|
#define AIObjLog(FuncParams) AI_LOG(AI,FuncParams)
|
|
VARARG_DECL(void,void,{},AILog,VARARG_NONE,const TCHAR*,VARARG_NONE,VARARG_NONE);
|
|
VARARG_DECL(void,void,{},AILog,VARARG_NONE,const TCHAR*,VARARG_EXTRA(enum EName E),VARARG_EXTRA(E));
|
|
#endif
|
|
|
|
UBOOL Tick( FLOAT DeltaTime, enum ELevelTick TickType );
|
|
|
|
virtual EGotoState GotoState( FName State, UBOOL bForceEvents = 0, UBOOL bKeepStack = 0 );
|
|
virtual void ProcessState( FLOAT DeltaSeconds );
|
|
|
|
virtual void StoreCommandHistory( UGameAICommand* Cmd );
|
|
/**
|
|
* DebugLog function which is called to log information specific to this AI (call NAVHANDLE_DEBUG_LOG macro, don't call this directly)
|
|
* @param LogText - text to log for this AI
|
|
*/
|
|
virtual void DebugLogInternal(const TCHAR* LogText)
|
|
{
|
|
AI_LOG(this,(LogText));
|
|
}
|
|
};
|
|
|
|
|
|
replication
|
|
{
|
|
if( bDemoRecording )
|
|
DemoActionString;
|
|
}
|
|
|
|
/** returns all AI Commands in the CommandList that are of the specified class or a subclass
|
|
* @note this function is only useful on the server
|
|
* @param BaseClass the base class of GameAICommand to return
|
|
* @param (out) Cmd the returned GameAICommand for each iteration
|
|
*/
|
|
native final iterator function AllCommands(class<GameAICommand> BaseClass, out GameAICommand Cmd);
|
|
|
|
/**
|
|
* PushCommand
|
|
* pushes a new AI command on top of the command stack
|
|
* @param NewCommand - command to place on top of stack
|
|
*/
|
|
native function PushCommand(GameAICommand NewCommand);
|
|
|
|
/**
|
|
* PopCommand
|
|
* will pop the passed command (and everything above it in the stack)
|
|
* @param ToBePoppedCommand - the command to pop
|
|
*/
|
|
native function PopCommand(GameAICommand ToBePoppedCommand);
|
|
|
|
/** AbortCommand
|
|
* Aborts a command (and all of its children)
|
|
* @param AbortCmd - the command to abort (can be NULL, in which case AbortClass will be used to determine which command to abort
|
|
* @param AbortClass - not used unless AbortCmd is NULL, in which case the first command int he stack of class 'AbortClass' will be aborted (and all its children)
|
|
*/
|
|
native function bool AbortCommand( GameAICommand AbortCmd, optional class<GameAICommand> AbortClass );
|
|
|
|
native final function GameAICommand GetActiveCommand();
|
|
/** checks the command stack for too many commands and/or infinite recursion */
|
|
native final function CheckCommandCount();
|
|
native final function DumpCommandStack();
|
|
/** finds and returns the lowest command of the specified class on the stack (will return subclasses of the specified class) */
|
|
native final function coerce GameAICommand FindCommandOfClass(class<GameAICommand> SearchClass) const;
|
|
|
|
/** This will search the CommandList for the passed in command class. **/
|
|
native function GameAICommand GetAICommandInStack( const class<GameAICommand> InClass );
|
|
|
|
/** return desired offset to move position... used for MoveTo/MoveToward */
|
|
function float GetDestinationOffset();
|
|
|
|
// called from movetogoal when we arrive at our destination
|
|
function ReachedMoveGoal();
|
|
function ReachedIntermediateMoveGoal();
|
|
|
|
/**
|
|
* =====
|
|
* DEBUG
|
|
* =====
|
|
*/
|
|
|
|
event Destroyed()
|
|
{
|
|
Super.Destroyed();
|
|
if (AILogFile != None)
|
|
{
|
|
AILogFile.Destroy();
|
|
}
|
|
|
|
// so this cases handles when AI has been destroyed. We need to give a chance to the AICmds to handle that case
|
|
if( Commandlist != None )
|
|
{
|
|
AbortCommand( CommandList );
|
|
}
|
|
}
|
|
|
|
protected function RecordDemoAILog( coerce string LogText );
|
|
|
|
|
|
`if(`__TW_PATHFINDING_)
|
|
event AILog_Internal( coerce string LogText, optional Name LogCategory, optional bool bForce, optional bool BugIt, optional bool bSkipExtraInfo )
|
|
`else
|
|
event AILog_Internal( coerce string LogText, optional Name LogCategory, optional bool bForce )
|
|
`endif
|
|
{
|
|
`if(`notdefined(FINAL_RELEASE))
|
|
local int Idx;
|
|
local String ActionStr, FinalStr;
|
|
local String FileName;
|
|
local GameAICommand ActiveCommand;
|
|
local int FileNameLength;
|
|
local Engine Eng;
|
|
Eng = class'Engine'.static.GetEngine();
|
|
|
|
|
|
if( Eng.bDisableAILogging )
|
|
{
|
|
return;
|
|
}
|
|
if( !bForce &&
|
|
!bAILogging )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (WorldInfo.IsConsoleBuild(CONSOLE_PS3))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if( !bForce )
|
|
{
|
|
for( Idx = 0; Idx < AILogFilter.Length; Idx++ )
|
|
{
|
|
if( AILogFilter[Idx] == LogCategory )
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (AILogFile == None)
|
|
{
|
|
AILogFile = Spawn(class'FileLog');
|
|
if (bMapBasedLogName)
|
|
{
|
|
FileName = WorldInfo.GetMapName()$"_"$string(self);
|
|
FileName = Repl(FileName,"ai_","",false);
|
|
}
|
|
else
|
|
{
|
|
FileName = string(self);
|
|
}
|
|
|
|
// for consoles we need to make certain the filename is short enough
|
|
if( class'WorldInfo'.static.GetWorldInfo().IsConsoleBuild() == TRUE )
|
|
{
|
|
// Include the file extension in determining the length
|
|
FileNameLength = Len(Filename) + 6;
|
|
// make sure file name isn't too long for consoles
|
|
if(FileNameLength > 40)
|
|
{
|
|
// Make sure there is room for the .ailog part too
|
|
FileName = Right(Filename,34);
|
|
}
|
|
}
|
|
|
|
AILogFile.bKillDuringLevelTransition = TRUE;
|
|
AILogFile.bFlushEachWrite = bFlushAILogEachLine;
|
|
// Use async unless flushing was requested
|
|
AILogFile.bWantsAsyncWrites = !bFlushAILogEachLine;
|
|
AILogFile.OpenLog(FileName,".ailog");
|
|
}
|
|
|
|
ActionStr = String(GetStateName());
|
|
ActiveCommand = GetActiveCommand();
|
|
if (ActiveCommand != None)
|
|
{
|
|
ActionStr = String(ActiveCommand.Class)$":"$String(ActiveCommand.GetStateName());
|
|
}
|
|
|
|
FinalStr = "["$WorldInfo.TimeSeconds$"]"@ActionStr$":"@LogText;
|
|
AILogFile.Logf(FinalStr);
|
|
|
|
if (WorldInfo.IsRecordingDemo())
|
|
{
|
|
RecordDemoAILog(FinalStr);
|
|
}
|
|
|
|
`Log( Pawn@"["$WorldInfo.TimeSeconds$"]"@ActionStr$":"@LogText, bAILogToWindow );
|
|
`endif
|
|
}
|
|
|
|
/** SetDesiredRotation
|
|
* Calls Pawn's SetDesiredRotation: Simple interface to Pawn
|
|
*/
|
|
function SetDesiredRotation(Rotator TargetDesiredRotation, bool InLockDesiredRotation=FALSE, bool InUnlockWhenReached=FALSE, FLOAT InterpolationTime=-1.f/*Use Default RotationRate*/)
|
|
{
|
|
if ( Pawn!=none )
|
|
{
|
|
Pawn.SetDesiredRotation(TargetDesiredRotation, InLockDesiredRotation, InUnlockWhenReached, InterpolationTime);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* ===========
|
|
* DEBUG STATES
|
|
* ===========
|
|
*/
|
|
state DEBUGSTATE
|
|
{
|
|
function BeginState( Name PreviousStateName )
|
|
{
|
|
//debug
|
|
`AILog( "BEGINSTATE"@PreviousStateName, 'State' );
|
|
}
|
|
|
|
function EndState( Name NextStateName )
|
|
{
|
|
//debug
|
|
`AILog( "ENDSTATE"@NextStateName, 'State' );
|
|
}
|
|
|
|
function PushedState()
|
|
{
|
|
//debug
|
|
`AILog( "PUSHED", 'State' );
|
|
}
|
|
|
|
function PoppedState()
|
|
{
|
|
//debug
|
|
`AILog( "POPPED", 'State' );
|
|
}
|
|
|
|
function ContinuedState()
|
|
{
|
|
//debug
|
|
`AILog( "CONTINUED", 'State' );
|
|
}
|
|
|
|
function PausedState()
|
|
{
|
|
//debug
|
|
`AILog( "PAUSED", 'State' );
|
|
}
|
|
}
|
|
|
|
|
|
/** @return a String with the Cmd and the State that the AI is in. **/
|
|
simulated final event string GetActionString()
|
|
{
|
|
local string ActionStr;
|
|
local GameAICommand ActiveCmd;
|
|
|
|
if( WorldInfo.IsPlayingDemo() )
|
|
{
|
|
return DemoActionString;
|
|
}
|
|
else
|
|
{
|
|
ActiveCmd = GetActiveCommand();
|
|
if( ActiveCmd != None )
|
|
{
|
|
ActionStr = string(ActiveCmd.Class)$":"$string(ActiveCmd.GetStateName());
|
|
}
|
|
else
|
|
{
|
|
ActionStr = string(default.Class)$":"$string(GetStateName());
|
|
}
|
|
|
|
return ActionStr;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
defaultproperties
|
|
{
|
|
|
|
} |