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