rott/kf_sources/Engine/Classes/Controller.uc
2026-07-14 20:27:09 +07:00

1102 lines
31 KiB
Ucode

//=============================================================================
// Controller, the base class of players or AI.
//
// Controllers are non-physical actors that can be attached to a pawn to control
// its actions. PlayerControllers are used by human players to control pawns, while
// AIControFllers implement the artificial intelligence for the pawns they control.
// Controllers take control of a pawn using their Possess() method, and relinquish
// control of the pawn by calling UnPossess().
//
// Controllers receive notifications for many of the events occuring for the Pawn they
// are controlling. This gives the controller the opportunity to implement the behavior
// in response to this event, intercepting the event and superceding the Pawn's default
// behavior.
//
// This is a built-in Unreal class and it shouldn't be modified.
//=============================================================================
class Controller extends Actor
config(user)
native
nativereplication
abstract;
var Pawn Pawn;
var const int PlayerNum; // The player number - per-match player number.
var float SightCounter; // Used to keep track of when to check player visibility
var float FovAngle; // X field of view angle in degrees, usually 90.
var globalconfig float Handedness;
var bool bIsPlayer; // Pawn is a player or a player-bot.
var bool bGodMode; // cheat - when true, can't be killed or hurt
//AI flags
var const bool bLOSflag; // used for alternating LineOfSight traces
var bool bAdvancedTactics; // serpentine movement between pathnodes
var bool bCanOpenDoors;
var bool bCanDoSpecial;
var bool bAdjusting; // adjusting around obstacle
var bool bPreparingMove; // set true while pawn sets up for a latent move
var bool bControlAnimations; // take control of animations from pawn (don't let pawn play animations based on notifications)
var bool bEnemyInfoValid; // false when change enemy, true when LastSeenPos etc updated
var bool bNotifyApex; // event NotifyJumpApex() when at apex of jump
var bool bUsePlayerHearing;
var bool bJumpOverWall; // true when jumping to clear obstacle
var bool bEnemyAcquired;
var bool bSoaking; // pause and focus on this bot if it encounters a problem
var bool bHuntPlayer; // hunting player
var bool bAllowedToTranslocate;
var bool bAllowedToImpactJump;
var bool bAdrenalineEnabled;
var bool bNotifyFallingHitWall;
var bool bSlowerZAcquire; // acquire targets above or below more slowly than at same height
var bool bInDodgeMove;
var bool bVehicleTransition;
var bool bForceStrafe;
var bool bNotifyPostLanded;
// Input buttons.
// if _RO_
var input byte
bRun, bFire, bAltFire, bVoiceTalk;
var byte bDuck; // toggle duck
// UT
//var input byte
// bRun, bDuck, bFire, bAltFire, bVoiceTalk;
// end if
var vector AdjustLoc; // location to move to while adjusting around obstacle
var const Controller nextController; // chained Controller list
var float Stimulus; // Strength of stimulus - Set when stimulus happens
// Navigation AI
var float MoveTimer;
var Actor MoveTarget; // actor being moved toward
var vector Destination; // location being moved toward
var vector FocalPoint; // location being looked at
var Actor Focus; // actor being looked at
var float FocusLead; // how much to lead view of focus
var Mover PendingMover; // mover pawn is waiting for to complete its move
var Actor GoalList[4]; // used by navigation AI - list of intermediate goals
var NavigationPoint home; // set when begin play, used for retreating and attitude checks
var float MinHitWall; // Minimum HitNormal dot Velocity.Normal to get a HitWall event from the physics
var float RespawnPredictionTime; // how far ahead to predict respawns when looking for inventory
var float DodgeToGoalPct; // Frequency bot tries to dodge to reachable goal
var int AcquisitionYawRate;
var float DodgeLandZ; // expected min landing height of dodge
var Vehicle LastBlockingVehicle;
// Enemy information
var Pawn Enemy;
var Actor Target;
var vector LastSeenPos; // enemy position when I last saw enemy (auto updated if EnemyNotVisible() enabled)
var vector LastSeeingPos; // position where I last saw enemy (auto updated if EnemyNotVisible enabled)
var float LastSeenTime;
var string VoiceType; // OBSOLETE
var float OldMessageTime; //to limit frequency of voice messages
// Route Cache for Navigation
var Actor RouteCache[16];
var ReachSpec CurrentPath;
var ReachSpec NextRoutePath;
var vector CurrentPathDir;
var Actor RouteGoal; //final destination for current route
var float RouteDist; // total distance for current route
var float LastRouteFind; // time at which last route finding occured
var vector DirectionHint; // used to pick which side of vehicle to get out on
// Replication Info
var() class<PlayerReplicationInfo> PlayerReplicationInfoClass;
var PlayerReplicationInfo PlayerReplicationInfo;
var class<Pawn> PawnClass; // class of pawn to spawn (for players)
var class<Pawn> PreviousPawnClass; // Holds the player's previous class
var float GroundPitchTime;
var vector ViewX, ViewY, ViewZ; // Viewrotation encoding for PHYS_Spider
var NavigationPoint StartSpot; // where player started the match
// for monitoring the position of a pawn
var vector MonitorStartLoc; // used by latent function MonitorPawn()
var Pawn MonitoredPawn; // used by latent function MonitorPawn()
var float MonitorMaxDistSq;
var AvoidMarker FearSpots[2]; // avoid these spots when moving
var float WarningDelay; // delay before act on firing warning
var Projectile WarningProjectile;
var Pawn ShotTarget;
var const Actor LastFailedReach; // cache to avoid trying failed actorreachable more than once per frame
var const float FailedReachTime;
var const vector FailedReachLocation;
var float Adrenaline;
var float AdrenalineMax; // maximum Adrenaline (combo energy)
var class<Weapon> LastPawnWeapon; // used by game stats
const LATENT_MOVETOWARD = 503; // LatentAction number for Movetoward() latent function
// Red Orchestra variables
// if _RO_
var input byte bSprint; // Sprinting
var byte bCrawl; // Prone
// end _RO_
replication
{
reliable if( bNetDirty && (Role==ROLE_Authority) )
PlayerReplicationInfo, Pawn;
reliable if( bNetDirty && (Role== ROLE_Authority) && bNetOwner )
PawnClass, Adrenaline,bAdrenalineEnabled;
// Functions the server calls on the client side.
reliable if( RemoteRole==ROLE_AutonomousProxy )
ClientGameEnded, ClientRoundEnded, ClientDying, ClientSetRotation, ClientSetLocation,
ClientSwitchToBestWeapon, ClientSetWeapon;
reliable if ( (!bDemoRecording || (bClientDemoRecording && bClientDemoNetFunc)) && Role == ROLE_Authority )
ClientVoiceMessage;
// Functions the client calls on the server.
unreliable if( Role<ROLE_Authority )
SendVoiceMessage, SetPawnClass;
reliable if ( Role < ROLE_Authority )
ServerRestartPlayer;
}
// Latent Movement.
//Note that MoveTo sets the actor's Destination, and MoveToward sets the
//actor's MoveTarget. Actor will rotate towards destination unless the optional ViewFocus is specified.
native(500) final latent function MoveTo( vector NewDestination, optional Actor ViewFocus, optional bool bShouldWalk);
native(502) final latent function MoveToward(actor NewTarget, optional Actor ViewFocus, optional float DestinationOffset, optional bool bUseStrafing, optional bool bShouldWalk);
native(508) final latent function FinishRotation();
// native AI functions
/* LineOfSightTo() returns true if any of several points of Other is visible
(origin, top, bottom)
*/
native(514) final function bool LineOfSightTo(actor Other);
/* CanSee() similar to line of sight, but also takes into account Pawn's peripheral vision
*/
native(533) final function bool CanSee(Pawn Other);
//Navigation functions - return the next path toward the goal
native(518) final function Actor FindPathTo(vector aPoint);
native(517) final function Actor FindPathToward(actor anActor, optional bool bWeightDetours);
native final function Actor FindPathToIntercept(Pawn P, Actor RouteGoal, optional bool bWeightDetours);
native final function Actor FindPathTowardNearest(class<NavigationPoint> GoalClass, optional bool bWeightDetours);
native(525) final function NavigationPoint FindRandomDest();
native(523) final function vector EAdjustJump(float BaseZ, float XYSpeed);
//Reachable returns whether direct path from Actor to aPoint is traversable
//using the current locomotion method
native(521) final function bool pointReachable(vector aPoint);
native(520) final function bool actorReachable(actor anActor);
/* PickWallAdjust()
Check if could jump up over obstruction (only if there is a knee height obstruction)
If so, start jump, and return current destination
Else, try to step around - return a destination 90 degrees right or left depending on traces
out and floor checks
*/
native(526) final function bool PickWallAdjust(vector HitNormal);
/* WaitForLanding()
latent function returns when pawn is on ground (no longer falling)
*/
native(527) final latent function WaitForLanding();
native(540) final function actor FindBestInventoryPath(out float MinWeight);
native final function actor FindBestSuperPickup(float MaxDist); // find nearest super pickup (base has bDelayedSpawn=true)
native(529) final function AddController();
native(530) final function RemoveController();
// Pick best pawn target
native(531) final function pawn PickTarget(out float bestAim, out float bestDist, vector FireDir, vector projStart, float MaxRange);
native(534) final function actor PickAnyTarget(out float bestAim, out float bestDist, vector FireDir, vector projStart);
native final function bool InLatentExecution(int LatentActionNumber); //returns true if controller currently performing latent action specified by LatentActionNumber
// Force end to sleep
native function StopWaiting();
native function EndClimbLadder();
native final function bool CanMakePathTo(Actor A); // assumes valid CurrentPath, tries to see if CurrentPath can be combine with path to N
event MayFall(); //return true if allowed to fall - called by engine when pawn is about to fall
event MayDodgeToMoveTarget();
event MissedDodge();
function PendingStasis()
{
bStasis = true;
Pawn = None;
}
// ----- combos ----- //
function AwardAdrenaline(float Amount)
{
if ( bAdrenalineEnabled )
{
Adrenaline += Amount;
Adrenaline = Clamp( Adrenaline, 0, AdrenalineMax );
}
}
function bool NeedsAdrenaline()
{
return ( (Pawn != None) && !Pawn.InCurrentCombo() && (Adrenaline < AdrenalineMax) );
}
/* DisplayDebug()
list important controller attributes on canvas
*/
function DisplayDebug(Canvas Canvas, out float YL, out float YPos)
{
local string DebugString;
if ( Pawn == None )
{
Super.DisplayDebug(Canvas,YL,YPos);
return;
}
Canvas.SetDrawColor(255,0,0);
Canvas.DrawText("CONTROLLER "$GetItemName(string(self))$" Pawn "$GetItemName(string(Pawn))$" viewpitch "$Rotation.Pitch);
YPos += YL;
Canvas.SetPos(4,YPos);
if ( Enemy != None )
Canvas.DrawText(" STATE: "$GetStateName()$" Timer: "$TimerCounter$" Enemy "$Enemy.GetHumanReadableName(), false);
else
Canvas.DrawText(" STATE: "$GetStateName()$" Timer: "$TimerCounter$" NO Enemy ", false);
YPos += YL;
Canvas.SetPos(4,YPos);
if ( MonitoredPawn != None )
DebugString $= " MonitoredPawn: "@MonitoredPawn.GetHumanReadableName();
else
DebugString $= " MonitoredPawn: None";
if ( Target != None )
DebugString $= " Target: "@Target.GetHumanReadableName();
else
DebugString $= " Target: None";
Canvas.DrawText(DebugString);
YPos += YL;
Canvas.SetPos(4,YPos);
if ( PlayerReplicationInfo == None )
Canvas.DrawText(" NO PLAYERREPLICATIONINFO", false);
else
PlayerReplicationInfo.DisplayDebug(Canvas,YL,YPos);
YPos += YL;
Canvas.SetPos(4,YPos);
}
simulated function String GetHumanReadableName()
{
if ( PlayerReplicationInfo != None )
return PlayerReplicationInfo.PlayerName;
return GetItemName(String(self));
}
simulated function rotator GetViewRotation()
{
return Rotation;
}
/* Reset()
reset actor to initial state
*/
function Reset()
{
super.Reset();
Enemy = None;
LastSeenTime = 0;
StartSpot = None;
Adrenaline = 0;
bAdjusting = false;
bPreparingMove = false;
bJumpOverWall = false;
bEnemyAcquired = false;
bHuntPlayer = false;
bInDodgeMove = false;
MoveTimer = -1;
MoveTarget = None;
PendingMover = None;
CurrentPath = None;
RouteGoal = None;
MonitoredPawn = None;
WarningProjectile = None;
}
function bool AvoidCertainDeath()
{
return false;
}
/* ClientSetLocation()
replicated function to set location and rotation. Allows server to force new location for
teleports, etc.
*/
function ClientSetLocation( vector NewLocation, rotator NewRotation )
{
SetRotation(NewRotation);
If ( (Rotation.Pitch > RotationRate.Pitch)
&& (Rotation.Pitch < 65536 - RotationRate.Pitch) )
{
If (Rotation.Pitch < 32768)
NewRotation.Pitch = RotationRate.Pitch;
else
NewRotation.Pitch = 65536 - RotationRate.Pitch;
}
if ( Pawn != None )
{
NewRotation.Roll = 0;
Pawn.SetRotation( NewRotation );
Pawn.SetLocation( NewLocation );
}
}
/* ClientSetRotation()
replicated function to set rotation. Allows server to force new rotation.
*/
function ClientSetRotation( rotator NewRotation )
{
SetRotation(NewRotation);
if ( Pawn != None )
{
NewRotation.Pitch = 0;
NewRotation.Roll = 0;
Pawn.SetRotation( NewRotation );
}
}
function ClientDying(class<DamageType> DamageType, vector HitLocation)
{
if ( Pawn != None )
{
Pawn.PlayDying(DamageType, HitLocation);
Pawn.GotoState('Dying');
}
}
/* AIHearSound()
Called when AI controlled pawn would hear a sound. Default AI implementation uses MakeNoise()
interface for hearing appropriate sounds instead
*/
event AIHearSound (
actor Actor,
int Id,
sound S,
vector SoundLocation,
vector Parameters,
bool Attenuate
);
event SoakStop(string problem);
function Possess(Pawn aPawn)
{
aPawn.PossessedBy(self);
Pawn = aPawn;
if ( PlayerReplicationInfo != None )
{
if ( Vehicle(Pawn) != None && Vehicle(Pawn).Driver != None )
PlayerReplicationInfo.bIsFemale = Vehicle(Pawn).Driver.bIsFemale;
else
PlayerReplicationInfo.bIsFemale = Pawn.bIsFemale;
}
// preserve Pawn's rotation initially for placed Pawns
FocalPoint = Pawn.Location + 512*vector(Pawn.Rotation);
Restart();
}
// unpossessed a pawn (not because pawn was killed)
function UnPossess()
{
if ( Pawn != None )
Pawn.UnPossessed();
Pawn = None;
}
function WasKilledBy(Controller Other);
function class<Weapon> GetLastWeapon()
{
if ( (Pawn == None) || (Pawn.Weapon == None) )
return LastPawnWeapon;
return Pawn.Weapon.Class;
}
/* PawnDied()
unpossess a pawn (because pawn was killed)
*/
function PawnDied(Pawn P)
{
if ( Pawn != P )
return;
if ( Pawn != None )
{
if ( Pawn.InCurrentCombo() )
Adrenaline = 0;
SetLocation(Pawn.Location);
Pawn.UnPossessed();
}
Pawn = None;
PendingMover = None;
if ( bIsPlayer )
{
if ( !IsInState('GameEnded') && !IsInState('RoundEnded') )
GotoState('Dead'); // can respawn
}
else
Destroy();
}
function Restart()
{
Enemy = None;
}
event LongFall(); // called when latent function WaitForLanding() doesn't return after 4 seconds
// notifications of pawn events (from C++)
// if return true, then pawn won't get notified
event bool NotifyPhysicsVolumeChange(PhysicsVolume NewVolume);
event bool NotifyHeadVolumeChange(PhysicsVolume NewVolume);
event bool NotifyLanded(vector HitNormal);
event NotifyPostLanded();
event bool NotifyHitWall(vector HitNormal, actor Wall);
event NotifyFallingHitWall(vector HitNormal, actor Wall); // only if bNotifyFallingHitWall is set
event bool NotifyBump(Actor Other);
event NotifyHitMover(vector HitNormal, mover Wall);
event NotifyJumpApex();
event NotifyMissedJump();
function SetDoubleJump();
// notifications called by pawn in script
function NotifyAddInventory(inventory NewItem);
function NotifyTakeHit(pawn InstigatedBy, vector HitLocation, int Damage, class<DamageType> damageType, vector Momentum)
{
if ( (instigatedBy != None) && (instigatedBy != pawn) )
damageAttitudeTo(instigatedBy, Damage);
}
function SetFall(); //about to fall
function PawnIsInPain(PhysicsVolume PainVolume); // called when pawn is taking pain volume damage
event PreBeginPlay()
{
AddController();
Super.PreBeginPlay();
if ( bDeleteMe )
return;
SightCounter = 0.2 * FRand(); //offset randomly
}
event PostBeginPlay()
{
Super.PostBeginPlay();
if ( !bDeleteMe && bIsPlayer && (Level.NetMode != NM_Client) )
{
PlayerReplicationInfo = Spawn(PlayerReplicationInfoClass, Self,,vect(0,0,0),rot(0,0,0));
InitPlayerReplicationInfo();
}
}
function InitPlayerReplicationInfo()
{
if (PlayerReplicationInfo.PlayerName == "")
PlayerReplicationInfo.SetPlayerName(class'GameInfo'.Default.DefaultPlayerName);
PlayerReplicationInfo.bNoTeam = !Level.Game.bTeamGame;
}
simulated function int GetTeamNum()
{
if ( (PlayerReplicationInfo == None) || (PlayerReplicationInfo.Team == None) )
return 255;
return PlayerReplicationInfo.Team.TeamIndex;
}
function bool SameTeamAs(Controller C)
{
if ( C == None )
return false;
return Level.Game.IsOnTeam(C,GetTeamNum());
}
function HandlePickup(Pickup pick)
{
if ( MoveTarget == pick )
{
if ( pick.MyMarker != None )
{
MoveTarget = pick.MyMarker;
Pawn.Anchor = pick.MyMarker;
MoveTimer = 0.5;
}
else
MoveTimer = -1.0;
}
}
simulated event Destroyed()
{
if ( Role < ROLE_Authority )
{
Super.Destroyed();
return;
}
RemoveController();
if ( bIsPlayer && (Level.Game != None) )
Level.Game.logout(self);
if ( PlayerReplicationInfo != None )
{
if ( !PlayerReplicationInfo.bOnlySpectator && (PlayerReplicationInfo.Team != None) )
PlayerReplicationInfo.Team.RemoveFromTeam(self);
PlayerReplicationInfo.Destroy();
}
Super.Destroyed();
}
event bool AllowDetourTo(NavigationPoint N)
{
return true;
}
/* AdjustView()
by default, check and see if pawn still needs to update eye height
(only if some playercontroller still has pawn as its viewtarget)
Overridden in playercontroller
*/
function AdjustView( float DeltaTime )
{
local Controller C;
for ( C=Level.ControllerList; C!=None; C=C.NextController )
if ( C.IsA('PlayerController') && (PlayerController(C).ViewTarget == Pawn) )
return;
Pawn.bUpdateEyeHeight =false;
Pawn.Eyeheight = Pawn.BaseEyeheight;
}
function bool WantsSmoothedView()
{
return ( (Pawn != None) && ((Pawn.Physics==PHYS_Walking) || (Pawn.Physics==PHYS_Spider)) && !Pawn.bJustLanded );
}
function GameHasEnded()
{
if ( Pawn != None )
Pawn.bNoWeaponFiring = true;
GotoState('GameEnded');
}
function ClientGameEnded()
{
GotoState('GameEnded');
}
function RoundHasEnded()
{
if ( Pawn != None )
Pawn.bNoWeaponFiring = true;
GotoState('RoundEnded');
}
function ClientRoundEnded()
{
GotoState('RoundEnded');
}
simulated event RenderOverlays( canvas Canvas );
/* GetFacingDirection()
returns direction faced relative to movement dir
0 = forward
16384 = right
32768 = back
49152 = left
*/
function int GetFacingDirection()
{
return 0;
}
//------------------------------------------------------------------------------
// Speech related
function byte GetMessageIndex(name PhraseName)
{
return 0;
}
function SendMessage(PlayerReplicationInfo Recipient, name MessageType, byte MessageID, float Wait, name BroadcastType)
{
// if _RO_
if (Pawn != none)
SendVoiceMessage(PlayerReplicationInfo, Recipient, MessageType, MessageID, BroadcastType, Pawn, Pawn.Location);
else
SendVoiceMessage(PlayerReplicationInfo, Recipient, MessageType, MessageID, BroadcastType, none, location);
// else
// SendVoiceMessage(PlayerReplicationInfo, Recipient, MessageType, MessageID, BroadcastType);
// end if _RO_
}
function bool AllowVoiceMessage(name MessageType)
{
if ( Level.TimeSeconds - OldMessageTime < 10 )
return false;
else
OldMessageTime = Level.TimeSeconds;
return true;
}
// if _RO_
function SendVoiceMessage(PlayerReplicationInfo Sender, PlayerReplicationInfo Recipient, name messagetype, byte messageID, name broadcasttype, optional Pawn soundSender, optional vector senderLocation)
// else
// function SendVoiceMessage(PlayerReplicationInfo Sender, PlayerReplicationInfo Recipient, name messagetype, byte messageID, name broadcasttype)
// end if _RO_
{
local Controller P;
if ( ((Recipient == None) || (AIController(self) == None))
&& !AllowVoiceMessage(MessageType) )
return;
for ( P=Level.ControllerList; P!=None; P=P.NextController )
{
if ( PlayerController(P) != None )
{
if ((P.PlayerReplicationInfo == Sender) ||
(P.PlayerReplicationInfo == Recipient &&
(Level.Game.BroadcastHandler == None ||
Level.Game.BroadcastHandler.AcceptBroadcastSpeech(PlayerController(P), Sender)))
)
// if _RO_
P.ClientVoiceMessage(Sender, Recipient, messagetype, messageID, soundSender, senderLocation);
// else
// P.ClientVoiceMessage(Sender, Recipient, messagetype, messageID);
// end if _RO_
else if ( (Recipient == None) || (Level.NetMode == NM_Standalone) )
{
if ( (broadcasttype == 'GLOBAL') || !Level.Game.bTeamGame || (Sender.Team == P.PlayerReplicationInfo.Team) )
if ( Level.Game.BroadcastHandler == None || Level.Game.BroadcastHandler.AcceptBroadcastSpeech(PlayerController(P), Sender) )
// if _RO_
P.ClientVoiceMessage(Sender, Recipient, messagetype, messageID, soundSender, senderLocation);
// else
// P.ClientVoiceMessage(Sender, Recipient, messagetype, messageID);
// end if _RO_
}
}
else if ( (messagetype == 'ORDER') && ((Recipient == None) || (Recipient == P.PlayerReplicationInfo)) )
P.BotVoiceMessage(messagetype, messageID, self);
}
}
// if _RO_
function ClientVoiceMessage(PlayerReplicationInfo Sender, PlayerReplicationInfo Recipient, name messagetype, byte messageID, optional Pawn soundSender, optional vector senderLocation);
// else
// function ClientVoiceMessage(PlayerReplicationInfo Sender, PlayerReplicationInfo Recipient, name messagetype, byte messageID);
// end if _RO_
function BotVoiceMessage(name messagetype, byte MessageID, Controller Sender);
//***************************************************************
// interface used by ScriptedControllers to query pending controllers
function bool WouldReactToNoise( float Loudness, Actor NoiseMaker)
{
return false;
}
function bool WouldReactToSeeing(Pawn Seen)
{
return false;
}
//***************************************************************
// AI related
/* AdjustToss()
return adjustment to Z component of aiming vector to compensate for arc given the target
distance
*/
function vector AdjustToss(float TSpeed, vector Start, vector End, bool bNormalize)
{
local vector Dest2D, Result, Vel2D;
local float Dist2D;
if ( Start.Z > End.Z + 64 )
{
Dest2D = End;
Dest2D.Z = Start.Z;
Dist2D = VSize(Dest2D - Start);
TSpeed *= Dist2D/VSize(End - Start);
Result = SuggestFallVelocity(Dest2D,Start,TSpeed,TSpeed);
Vel2D = result;
Vel2D.Z = 0;
Result.Z = Result.Z + (End.Z - Start.Z) * VSize(Vel2D)/Dist2D;
}
else
{
Result = SuggestFallVelocity(End,Start,TSpeed,TSpeed);
}
if ( bNormalize )
return TSpeed * Normal(Result);
else
return Result;
}
event PrepareForMove(NavigationPoint Goal, ReachSpec Path);
function WaitForMover(Mover M);
function MoverFinished();
function UnderLift(Mover M);
function FearThisSpot(AvoidMarker aSpot)
{
local int i;
if ( Pawn == None )
return;
if ( !LineOfSightTo(aSpot) )
return;
for ( i=0; i<2; i++ )
if ( FearSpots[i] == None )
{
FearSpots[i] = aSpot;
return;
}
for ( i=0; i<2; i++ )
if ( VSize(Pawn.Location - FearSpots[i].Location) > VSize(Pawn.Location - aSpot.Location) )
{
FearSpots[i] = aSpot;
return;
}
}
event float Desireability(Pickup P)
{
return P.BotDesireability(Pawn);
}
event float SuperDesireability(Pickup P)
{
return P.BotDesireability(Pawn);
}
/* called before start of navigation network traversal to allow setup of transient navigation flags
*/
event SetupSpecialPathAbilities();
event HearNoise( float Loudness, Actor NoiseMaker);
event SeePlayer( Pawn Seen ); // called when a player (bIsPlayer==true) pawn is seen
event SeeMonster( Pawn Seen ); // called when a non-player (bIsPlayer==false) pawn is seen
event EnemyNotVisible();
function DamageShake(int damage);
function ShakeView(vector shRotMag, vector shRotRate, float shRotTime,
vector shOffsetMag, vector shOffsetRate, float shOffsetTime);
function NotifyKilled(Controller Killer, Controller Killed, pawn Other)
{
if ( Enemy == Other )
Enemy = None;
}
function damageAttitudeTo(pawn Other, float Damage);
function float AdjustDesireFor(Pickup P);
function bool FireWeaponAt(Actor A);
function StopFiring()
{
if ( Pawn != None )
Pawn.StopWeaponFiring();
bFire = 0;
bAltFire = 0;
}
simulated function float RateWeapon(Weapon w)
{
return w.Default.Priority;
}
function float WeaponPreference(Weapon W)
{
return 0.0;
}
/* AdjustAim()
AIController version does adjustment for non-controlled pawns.
PlayerController version does the adjustment for player aiming help.
Only adjusts aiming at pawns
allows more error in Z direction (full as defined by AutoAim - only half that difference for XY)
*/
function rotator AdjustAim(FireProperties FiredAmmunition, vector projStart, int aimerror)
{
return Rotation;
}
function InstantWarnTarget(Actor Target, FireProperties FiredAmmunition, vector FireDir)
{
local float Dist;
if ( FiredAmmunition.bInstantHit && (Pawn(Target) != None) && (Pawn(Target).Controller != None) )
{
Dist = VSize(Pawn.Location - Target.Location);
if ( VSize(FireDir * Dist - Target.Location) < Target.CollisionRadius )
return;
if ( FRand() < FiredAmmunition.WarnTargetPct )
Pawn(Target).Controller.ReceiveWarning(Pawn, -1, FireDir);
return;
}
}
/* ReceiveWarning()
AI controlled creatures may duck
if not falling, and projectile time is long enough
often pick opposite to current direction (relative to shooter axis)
*/
event ReceiveWarning(Pawn shooter, float projSpeed, vector FireDir);
function ReceiveProjectileWarning(Projectile proj)
{
if ( WarningProjectile == None )
ReceiveWarning(Proj.Instigator, Proj.speed, Normal(Proj.Velocity));
}
/* If ReceiveWarning caused WarningDelay to be set, this will be called when it times out
*/
event DelayedWarning();
exec function SwitchToBestWeapon()
{
local float rating;
if ( Pawn == None || Pawn.Inventory == None )
return;
if ( (Pawn.PendingWeapon == None) || (AIController(self) != None) )
{
Pawn.PendingWeapon = Pawn.Inventory.RecommendWeapon(rating);
if ( Pawn.PendingWeapon == Pawn.Weapon )
Pawn.PendingWeapon = None;
if ( Pawn.PendingWeapon == None )
return;
}
StopFiring();
if ( Pawn.Weapon == None )
Pawn.ChangedWeapon();
else if ( Pawn.Weapon != Pawn.PendingWeapon )
{
Pawn.Weapon.PutDown();
}
}
// server calls this to force client to switch
function ClientSwitchToBestWeapon()
{
SwitchToBestWeapon();
}
function ClientSetWeapon( class<Weapon> WeaponClass )
{
local Inventory Inv;
local int Count;
for( Inv = Pawn.Inventory; Inv != None; Inv = Inv.Inventory )
{
Count++;
if ( Count > 1000 )
return;
if( !ClassIsChildOf( Inv.Class, WeaponClass ) )
continue;
if( Pawn.Weapon == None )
{
Pawn.PendingWeapon = Weapon(Inv);
Pawn.ChangedWeapon();
}
else if ( Pawn.Weapon != Weapon(Inv) )
{
Pawn.PendingWeapon = Weapon(Inv);
Pawn.Weapon.PutDown();
}
return;
}
}
function SetPawnClass(string inClass, string inCharacter)
{
local class<Pawn> pClass;
if ( inClass == "" )
return;
pClass = class<Pawn>(DynamicLoadObject(inClass, class'Class'));
if ( pClass != None )
PawnClass = pClass;
}
function SetPawnFemale();
function bool CheckFutureSight(float DeltaTime)
{
return true;
}
function ChangedWeapon()
{
if ( Pawn.Weapon != None )
LastPawnWeapon = Pawn.Weapon.Class;
}
function ServerReStartPlayer()
{
if ( Level.NetMode == NM_Client )
return;
if ( Pawn != None )
ServerGivePawn();
}
function ServerGivePawn();
event MonitoredPawnAlert();
function StartMonitoring(Pawn P, float MaxDist)
{
MonitoredPawn = P;
MonitorStartLoc = P.Location;
MonitorMaxDistSq = MaxDist * MaxDist;
}
function bool AutoTaunt()
{
return false;
}
function bool DontReuseTaunt(int T)
{
return false;
}
// - ParseChatPercVar should be subclassed if a controller needs more of them
function string ParseChatPercVar(string Cmd)
{
if (cmd~="%A") // Adrenaline
return int(Adrenaline)@"Adrenaline";
if ( (Pawn != None) && (cmd~="%S") ) // Shield
return int(Pawn.ShieldStrength)@"Shield";
return Cmd;
}
// **********************************************
// Controller States
State Dead
{
ignores SeePlayer, HearNoise, KilledBy;
function PawnDied(Pawn P)
{
if ( Level.NetMode != NM_Client )
warn(self$" Pawndied while dead");
}
function ServerReStartPlayer()
{
if ( Level.NetMode == NM_Client )
return;
Level.Game.RestartPlayer(self);
}
}
state GameEnded
{
ignores SeePlayer, HearNoise, KilledBy, NotifyBump, HitWall, NotifyPhysicsVolumeChange, NotifyHeadVolumeChange, Falling, TakeDamage, ReceiveWarning;
function BeginState()
{
if ( Pawn != None )
{
if ( Pawn.Weapon != None )
Pawn.Weapon.HolderDied();
Pawn.SimAnim.AnimRate = 0;
Pawn.TurnOff();
Pawn.UnPossessed();
Pawn = None;
}
if ( !bIsPlayer )
Destroy();
}
}
state RoundEnded
{
ignores SeePlayer, HearNoise, KilledBy, NotifyBump, HitWall, NotifyPhysicsVolumeChange, NotifyHeadVolumeChange, Falling, TakeDamage, ReceiveWarning;
function BeginState()
{
if ( Pawn != None )
{
if ( Pawn.Weapon != None )
Pawn.Weapon.HolderDied();
Pawn.SimAnim.AnimRate = 0;
Pawn.TurnOff();
Pawn.UnPossessed();
Pawn = None;
}
if ( !bIsPlayer )
Destroy();
}
}
defaultproperties
{
RotationRate=(Pitch=3072,Yaw=30000,Roll=2048)
AcquisitionYawRate=20000
FovAngle=+00090.000000
bHidden=true
bHiddenEd=true
PlayerReplicationInfoClass=Class'Engine.PlayerReplicationInfo'
MinHitWall=-1.f
Adrenaline=0.0
AdrenalineMax=100.0
bAdrenalineEnabled=true
bSlowerZAcquire=true
RemoteRole=ROLE_None
bOnlyRelevantToOwner=true
//if _RO_
// So traces from this controller don't hit aux collision cylinders
bBlockHitPointTraces=false
}