468 lines
10 KiB
Ucode
468 lines
10 KiB
Ucode
// 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
|
|
}
|