// ScriptedController // AI controller which is controlling the pawn through a scripted sequence specified by // an AIScript class ScriptedController extends AIController; var controller PendingController; // controller which will get this pawn after scripted sequence is complete var int ActionNum; var int AnimsRemaining; var ScriptedSequence SequenceScript; var LatentScriptedAction CurrentAction; var Action_PLAYANIM CurrentAnimation; var bool bBroken; var bool bShootTarget; var bool bShootSpray; var bool bPendingShoot; var bool bFakeShot; // FIXME - this is currently a hack var bool bUseScriptFacing; var bool bPendingDoubleJump; var bool bFineWeaponControl; var bool bChangingPawns; var Actor ScriptedFocus; var PlayerController MyPlayerController; var int NumShots; var name FiringMode; var int IterationCounter; var int IterationSectionStart; function bool WeaponFireAgain(float RefireRate, bool bFinishedFire) { return bFineWeaponControl; } function SetDoubleJump() { bNotifyApex = true; bPendingDoubleJump = true; } event NotifyJumpApex() { local actor HitActor; local vector HitNormal,HitLocation, HalfHeight,Start; // double jump if ( bPendingDoubleJump ) { Pawn.bWantsToCrouch = false; if ( Pawn.CanDoubleJump() ) Pawn.DoDoubleJump(false); bPendingDoubleJump = false; } else if ( bJumpOverWall ) { // double jump if haven't cleared obstacle Pawn.Acceleration = Destination - Pawn.Location; Pawn.Acceleration.Z = 0; HalfHeight = Pawn.GetCollisionExtent(); HalfHeight.Z *= 0.5; Start = Pawn.Location - Pawn.CollisionHeight * vect(0,0,0.5); HitActor = Pawn.Trace(HitLocation, HitNormal, Start + 8 * Normal(Pawn.Acceleration), Start, true,HalfHeight); if ( HitActor != None ) { Pawn.bWantsToCrouch = false; if ( Pawn.CanDoubleJump() ) Pawn.DoDoubleJump(false); } } } function TakeControlOf(Pawn aPawn) { if ( Pawn != aPawn ) { aPawn.PossessedBy(self); Pawn = aPawn; } GotoState('Scripting'); } function SetEnemyReaction(int AlertnessLevel); function DestroyPawn() { if ( Pawn != None ) Pawn.Destroy(); Destroy(); } function Pawn GetMyPlayer() { if ( (MyPlayerController == None) || (MyPlayerController.Pawn == None) ) ForEach DynamicActors(class'PlayerController',MyPlayerController) if ( MyPlayerController.Pawn != None ) break; if ( MyPlayerController == None ) return None; return MyPlayerController.Pawn; } function Pawn GetInstigator() { if ( Pawn != None ) return Pawn; return Instigator; } function Actor GetSoundSource() { if ( Pawn != None ) return Pawn; return SequenceScript; } function bool CheckIfNearPlayer(float Distance) { local Pawn MyPlayer; MyPlayer = GetMyPlayer(); return ( (MyPlayer != None) && (VSize(Pawn.Location - MyPlayer.Location) < Distance+CollisionRadius+MyPlayer.CollisionRadius ) && Pawn.PlayerCanSeeMe() ); } function ClearScript() { ActionNum = 0; CurrentAction = None; CurrentAnimation = None; ScriptedFocus = None; Pawn.SetWalking(false); Pawn.ShouldCrouch(false); // if _RO_ Pawn.ShouldProne(false); // endif _RO_ } function SetNewScript(ScriptedSequence NewScript) { MyScript = NewScript; SequenceScript = NewScript; Focus = None; ClearScript(); SetEnemyReaction(3); SequenceScript.SetActions(self); } function ClearAnimation() { AnimsRemaining = 0; bControlAnimations = false; CurrentAnimation = None; Pawn.PlayWaiting(); } function int SetFireYaw(int FireYaw) { FireYaw = FireYaw & 65535; if ( Pawn.Physics != PHYS_None && Pawn.Physics != PHYS_Karma && (Abs(FireYaw - (Rotation.Yaw & 65535)) > 8192) && (Abs(FireYaw - (Rotation.Yaw & 65535)) < 57343) ) { if ( FireYaw ClockwiseFrom Rotation.Yaw ) FireYaw = Rotation.Yaw + 8192; else FireYaw = Rotation.Yaw - 8192; } return FireYaw; } function rotator AdjustAim(FireProperties FiredAmmunition, vector projStart, int AimError) { local rotator LookDir; // make sure bot has a valid target if ( Target == None ) Target = ScriptedFocus; if ( Target == None ) { Target = Enemy; if ( Target == None ) return Pawn.Rotation; } LookDir = rotator(Target.Location - projStart); LookDir.Yaw = SetFireYaw(LookDir.Yaw); return LookDir; } function LeaveScripting(); state Scripting { function DisplayDebug(Canvas Canvas, out float YL, out float YPos) { Super.DisplayDebug(Canvas,YL,YPos); Canvas.DrawText("AIScript "$SequenceScript$" ActionNum "$ActionNum, false); YPos += YL; Canvas.SetPos(4,YPos); CurrentAction.DisplayDebug(Canvas,YL,YPos); } /* UnPossess() scripted sequence is over - return control to PendingController */ function UnPossess() { Pawn.UnPossessed(); if ( (Pawn != None) && (PendingController != None) ) { PendingController.bStasis = false; PendingController.Possess(Pawn); } Pawn = None; if ( !bChangingPawns ) Destroy(); } function LeaveScripting() { UnPossess(); } function InitForNextAction() { SequenceScript.SetActions(self); if ( CurrentAction == None ) { LeaveScripting(); return; } MyScript = SequenceScript; if ( CurrentAnimation == None ) ClearAnimation(); } function Trigger( actor Other, pawn EventInstigator ) { if ( CurrentAction.CompleteWhenTriggered() ) CompleteAction(); } function Timer() { if ( CurrentAction.WaitForPlayer() && CheckIfNearPlayer(CurrentAction.GetDistance()) ) CompleteAction(); else if ( CurrentAction.CompleteWhenTimer() ) CompleteAction(); } function AnimEnd(int Channel) { if ( CurrentAction != none && CurrentAction.CompleteOnAnim(Channel) ) { CompleteAction(); return; } if ( Channel == 0 ) { if ( (CurrentAnimation == None) || !CurrentAnimation.PawnPlayBaseAnim(self,false) ) ClearAnimation(); } else { // FIXME - support for CurrentAnimation play on other channels Pawn.AnimEnd(Channel); } } // ifdef WITH_LIPSINC function LIPSincAnimEnd() { if ( CurrentAction.CompleteOnLIPSincAnim() ) { CompleteAction(); return; } else { Pawn.LIPSincAnimEnd(); } } // endif function CompleteAction() { CurrentAction.ActionCompleted(); ActionNum++; GotoState('Scripting','Begin'); } function SetMoveTarget() { local Actor NextMoveTarget; Focus = ScriptedFocus; NextMoveTarget = CurrentAction.GetMoveTargetFor(self); if ( NextMoveTarget == None ) { GotoState('Broken'); return; } if ( Focus == None ) Focus = NextMoveTarget; MoveTarget = NextMoveTarget; if ( !ActorReachable(MoveTarget) ) { MoveTarget = FindPathToward(MoveTarget,false); if ( Movetarget == None ) { AbortScript(); return; } if ( Focus == NextMoveTarget ) Focus = MoveTarget; } } function AbortScript() { LeaveScripting(); } /* WeaponFireAgain() Notification from weapon when it is ready to fire (either just finished firing, or just finished coming up/reloading). Returns true if weapon should fire. If it returns false, can optionally set up a weapon change */ function bool WeaponFireAgain(float RefireRate, bool bFinishedFire) { if ( bFineWeaponControl ) return true; if ( Pawn.bIgnorePlayFiring ) { Pawn.bIgnorePlayFiring = false; return false; } if ( NumShots < 0 ) { bShootTarget = false; bShootSpray = false; StopFiring(); return false; } if ( bShootTarget && (ScriptedFocus != None) && !ScriptedFocus.bDeleteMe ) { Target = ScriptedFocus; if ( (!bShootSpray && ((Pawn.Weapon.RefireRate() < 0.99) && !Pawn.Weapon.CanAttack(Target))) || !Pawn.Weapon.BotFire(bFinishedFire,FiringMode) ) { Enable('Tick'); //FIXME - use multiple timer for this instead bPendingShoot = true; return false; } if ( NumShots > 0 ) { NumShots--; if ( NumShots == 0 ) NumShots = -1; } return true; } StopFiring(); return false; } function Tick(float DeltaTime) { if ( bPendingShoot ) { bPendingShoot = false; MayShootTarget(); } if ( !bPendingShoot && ((CurrentAction == None) || !CurrentAction.StillTicking(self,DeltaTime)) ) disable('Tick'); } function MayShootAtEnemy(); function MayShootTarget() { WeaponFireAgain(0,false); } function EndState() { bUseScriptFacing = true; bFakeShot = false; } Begin: InitforNextAction(); if ( bBroken ) GotoState('Broken'); if ( CurrentAction.TickedAction() ) enable('Tick'); if ( !bFineWeaponControl ) { if ( !bShootTarget ) { bFire = 0; bAltFire = 0; } else { Pawn.Weapon.RateSelf(); if ( bShootSpray ) MayShootTarget(); } } if ( CurrentAction.MoveToGoal() ) { Pawn.SetMovementPhysics(); WaitForLanding(); KeepMoving: SetMoveTarget(); MayShootTarget(); if ( (MoveTarget != None) && (MoveTarget != Pawn) ) { MoveToward(MoveTarget, Focus,,,Pawn.bIsWalking); if ( (MoveTarget != CurrentAction.GetMoveTargetFor(self)) || !Pawn.ReachedDestination(CurrentAction.GetMoveTargetFor(self)) ) Goto('KeepMoving'); } CompleteAction(); } else if ( CurrentAction.TurnToGoal() ) { Pawn.SetMovementPhysics(); Focus = CurrentAction.GetMoveTargetFor(self); if ( Focus == None ) FocalPoint = Pawn.Location + 1000 * vector(SequenceScript.Rotation); FinishRotation(); CompleteAction(); } else { //Pawn.SetPhysics(PHYS_RootMotion); Pawn.Acceleration = vect(0,0,0); Focus = ScriptedFocus; if ( !bUseScriptFacing ) FocalPoint = Pawn.Location + 1000 * vector(Pawn.Rotation); else if ( Focus == None ) { MayShootAtEnemy(); FocalPoint = Pawn.Location + 1000 * vector(SequenceScript.Rotation); } FinishRotation(); MayShootTarget(); } } // Broken scripted sequence - for debugging State Broken { Begin: warn(Pawn$" Scripted Sequence BROKEN "$SequenceScript$" ACTION "$CurrentAction); Pawn.bPhysicsAnimUpdate = false; Pawn.StopAnimating(); if ( GetMyPlayer() != None ) PlayerController(GetMyPlayer().Controller).SetViewTarget(Pawn); } defaultproperties { bUseScriptFacing=true IterationSectionStart=-1 }