//============================================================================= // 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 PlayerReplicationInfoClass; var PlayerReplicationInfo PlayerReplicationInfo; var class PawnClass; // class of pawn to spawn (for players) var class 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 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 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, 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 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, 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 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 pClass; if ( inClass == "" ) return; pClass = class(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 }