2020-12-13 15:01:13 +00:00
//=============================================================================
// PlayerController
//
// PlayerControllers are used by human players to control pawns.
//
// This is a built-in Unreal class and it shouldn't be modified.
// for the change in Possess().
// Copyright 1998-2013 Epic Games, Inc. All Rights Reserved.
//=============================================================================
class PlayerController extends Controller
config ( Game )
native ( Controller )
nativereplication
dependson ( OnlineSubsystem , OnlineGameSearch , SeqAct _ControlMovieTexture ) ;
var const Player Player ; // Player info
var ( Camera ) editinline Camera PlayerCamera ; // Camera associated with this Player Controller
var const class < Camera > CameraClass ; // Camera class to use for the PlayerCamera
// Player control flags
var bool bFrozen ; // Set when game ends or player dies to temporarily prevent player from restarting (until cleared by timer)
var bool bPressedJump ;
var bool bDoubleJump ;
var bool bUpdatePosition ;
var bool bUpdating ;
var globalconfig bool bNeverSwitchOnPickup ; // If true, don't automatically switch to picked up weapon
var bool bCheatFlying ; // instantly stop in flying mode
var bool bCameraPositionLocked ;
var bool bShortConnectTimeOut ; // when true, reduces connect timeout to 15 seconds
var const bool bPendingDestroy ; // when true, playercontroller is being destroyed
var bool bWasSpeedHack ;
var const bool bWasSaturated ; // used by servers to identify saturated client connections
var globalconfig bool bAimingHelp ;
var float MaxResponseTime ; // how long server will wait for client move update before setting position
var float WaitDelay ; // Delay time until can restart
var pawn AcknowledgedPawn ; // Used in net games so client can acknowledge it possessed a pawn
var eDoubleClickDir DoubleClickDir ; // direction of movement key double click (for special moves)
// Camera info.
var const actor ViewTarget ;
var PlayerReplicationInfo RealViewTarget ;
var transient bool bCameraCut ; // Whether we did a camera cut this frame. Automatically reset to FALSE every frame.
/** True if clients are handling setting their own viewtarget and the server should not replicate it (e.g. during certain matinees) */
var bool bClientSimulatingViewTarget ;
/** Director track that's currently possessing this player controller, or none if not possessed. */
var transient InterpTrackInstDirector ControllingDirTrackInst ;
/** field of view angle in degrees */
var protected float FOVAngle ;
var float DesiredFOV ;
var float DefaultFOV ;
/ * * l a s t u s e d F O V b a s e d m u l t i p l i e r t o d i s t a n c e t o a n o b j e c t w h e n d e t e r m i n i n g i f i t e x c e e d s t h e o b j e c t ' s c u l l d i s t a n c e
* @ note : only valid on client
* /
var const float LODDistanceFactor ;
// Remote Pawn ViewTargets
var rotator TargetViewRotation ;
var float TargetEyeHeight ;
/** used for smoothing the viewrotation of spectated players */
var rotator BlendedTargetViewRotation ;
var HUD myHUD ; // heads up display info
var HUD mySecondaryHUD ; // secondary heads up display info
// Move buffering for network games. Clients save their un-acknowledged moves in order to replay them
// when they get position updates from the server.
/** SavedMoveClass should be changed for network player move replication where different properties need to be replicated from the base engine implementation.*/
var class < SavedMove > SavedMoveClass ;
var SavedMove SavedMoves ; // buffered moves pending position updates
var SavedMove FreeMoves ; // freed moves, available for buffering
var SavedMove PendingMove ; // PendingMove already processed on client - waiting to combine with next movement to reduce client to server bandwidth
var vector LastAckedAccel ; // last acknowledged sent acceleration
var float CurrentTimeStamp ; // Timestamp on server of most recent servermove() processed for this playercontroller
var float LastUpdateTime ; // Last time server updated client with a move correction or confirmation
var float ServerTimeStamp ; // Server clock time when last servermove was received
var float TimeMargin ; // Difference between server's clock and time of received servermoves - used for speedhack detection
var float ClientUpdateTime ; // Client timestamp of last time it sent a servermove() to the server. Used for holding off on sending movement updates to save bandwidth.
var float MaxTimeMargin ; // Max time difference before detecting speedhack. . Calculated from GameInfo speedhack detection configurable settings.
var float LastActiveTime ; // used to kick idlers
/** Cap set by server on bandwidth from client to server in bytes/sec (only has impact if >=2600) */
var int ClientCap ;
/** ping replication and netspeed adjustment based on ping */
var deprecated float DynamicPingThreshold ;
/** Client time when last ping update was sent to server. */
var float LastPingUpdate ;
/** Last time possible speedhack was logged in server log. */
var float LastSpeedHackLog ;
/** MAXPOSITIONERRORSQUARED is the square of the max position error that is accepted (not corrected) in net play */
const MAXPOSITIONERRORSQUARED = 3.0 ;
/** MAXNEARZEROVELOCITYSQUARED is the square of the max velocity that is considered zero (not corrected) in net play */
const MAXNEARZEROVELOCITYSQUARED = 9.0 ;
/** MAXVEHICLEPOSITIONERRORSQUARED is the square of the max position error that is accepted (not corrected) in net play when driving a vehicle*/
const MAXVEHICLEPOSITIONERRORSQUARED = 900.0 ;
/ * * C L I E N T A D J U S T U P D A T E C O S T i s t h e b a n d w i d t h c o s t i n b y t e s o f s e n d i n g a c l i e n t a d j u s t m e n t u p d a t e . 1 8 0 i s g r e a t e r t h a n t h e a c t u a l c o s t , b u t r e p r e s e n t s a t w e a k e d v a l u e r e s e r v i n g e n o u g h b a n d w i d t h f o r
other updates sent to the client . Increase this value to reduce client adjustment update frequency , or if the amount of data sent in the clientadjustment ( ) call increases * /
const CLIENTADJUSTUPDATECOST = 180.0 ;
/** MAXCLIENTUPDATEINTERVAL is the maximum time between movement updates from the client before the server forces an update. */
const MAXCLIENTUPDATEINTERVAL = 0.25 ;
// ClientAdjustPosition replication (event called at end of frame)
struct native ClientAdjustment
{
var float TimeStamp ;
var EPhysics newPhysics ;
var vector NewLoc ;
var vector NewVel ;
var actor NewBase ;
var vector NewFloor ;
var byte bAckGoodMove ;
} ;
var ClientAdjustment PendingAdjustment ;
var int GroundPitch ;
// Components ( inner classes )
var transient CheatManager CheatManager ; // Object within playercontroller that manages "cheat" commands
var class < CheatManager > CheatClass ; // class of my CheatManager
var ( ) transient editinline PlayerInput PlayerInput ; // Object within playercontroller that manages player input.
var class < PlayerInput > InputClass ; // class of my PlayerInput
var const vector FailedPathStart ;
var CylinderComponent CylinderComponent ;
// Manages gamepad rumble (within)
var config string ForceFeedbackManagerClassName ;
var transient ForceFeedbackManager ForceFeedbackManager ;
// Interactions.
var transient array < interaction > Interactions ;
/** Indicates that the server and client */
var bool bHasVoiceHandshakeCompleted ;
/** List of players that are explicitly muted (outside of gameplay) */
var array < UniqueNetId > VoiceMuteList ;
/** List of players muted via gameplay */
var array < UniqueNetId > GameplayVoiceMuteList ;
/** The list of combined players to filter voice packets for */
var array < UniqueNetId > VoicePacketFilter ;
/** Info about a connected peer relative to this player */
struct native ConnectedPeerInfo
{
/** Unique net id for the remote peer player */
var UniqueNetId PlayerId ;
/** NAT type of remote peer connection */
var ENATType NATType ;
/** TRUE if remote peer has lost connecttion to the game host */
var bool bLostConnectionToHost ;
} ;
/** List of net ids for peer connections relative to this player. If a player is missing a connected peer then voice packets are routed through server. */
var array < ConnectedPeerInfo > ConnectedPeers ;
/** Sorted list of peers to be the next host. Client peers will determine if they are the new host by looking at this list. */
var array < UniqueNetId > BestNextHostPeers ;
/** Holds the migrated session sent by the new host. Client peers will join this migrated session. */
var OnlineGameSearch MigratedSearchToJoin ;
/** Cached online subsystem variable */
var OnlineSubsystem OnlineSub ;
//@HSL_BEGIN - BWJ - 8-4-16 - Playfab support
var PlayfabInterface PlayfabInter ;
//@HSL_END
/** Cached online voice interface variable */
var OnlineVoiceInterface VoiceInterface ;
/** The data store that holds any online player data */
var UIDataStore _OnlinePlayerData OnlinePlayerData ;
//@HSL_BEGIN_XBOX
/** Cached result for CanPlayOnline */
var bool bCanPlayOnline ;
//@SABER_EGS_BEGIN Crossplay support
var bool bIsEosPlayer ;
//@SABER_EGS_END
/** Cached result for CanShareUserCreatedContent */
var bool bCanShareUserCreatedContent ;
/** Cached result for CanCommunicateVoice */
var bool bCanCommunicateVoice ;
/** bool to use before checking privileges */
var bool bPrivilegesInitialized ;
//@HSL_END_XBOX
//@HSL_BEGIN - JRO - 6/17/2015 - Keep this around so we can still join the game after leaving the current one
/** Save the invite result so a later delegate can use it to join the online game */
var OnlineGameSearchResult CachedInviteResult ;
//@HLS_END
/** Ignores movement input. Stacked state storage, Use accessor function IgnoreMoveInput() */
var byte bIgnoreMoveInput ;
/** Ignores look input. Stacked state storage, use accessor function IgnoreLookInput(). */
var byte bIgnoreLookInput ;
/** Maximum distance to search for interactable actors */
var config float InteractDistance ;
/** Is this player currently in cinematic mode? Prevents rotation/movement/firing/etc */
var bool bCinematicMode ;
/ * *
* Whether this player is currently in an interactive sequence .
* This is set to false if there is any matinee active with a director track and a camera cut .
* /
var bool bInteractiveMode ;
/** The state of the inputs from cinematic mode */
var bool bCinemaDisableInputMove , bCinemaDisableInputLook ;
//@HSL_BEGIN_XBOX
/** Whether or not to render the UI within the cinematic black bars */
var bool bRenderHUDFullScreen ;
//@HSL_END_XBOX
/** Used to cache the session name to join until the timer fires */
var name DelayedJoinSessionName ;
/** Whether to ignore network error messages from now on */
var bool bIgnoreNetworkMessages ;
/** Used to toggle pushing DrawText Kismet events to the Hud's KismetTextInfo */
var config bool bShowKismetDrawText ;
// PLAYER INPUT MATCHING =============================================================
/** Type of inputs the matching code recognizes */
enum EInputTypes
{
IT _XAxis ,
IT _YAxis ,
} ;
/** How to match an input action */
enum EInputMatchAction
{
IMA _GreaterThan ,
IMA _LessThan
} ;
/** Individual entry to input matching sequences */
struct native InputEntry
{
/** Type of input to match */
var EInputTypes Type ;
/** Min value required to consider as a valid match */
var float Value ;
/** Max amount of time since last match before sequence resets */
var float TimeDelta ;
/** What type of match is this? */
var EInputMatchAction Action ;
} ;
/ * *
* Contains information to match a series of a inputs and call the given
* function upon a match . Processed by PlayerInput , defined in the
* PlayerController .
* /
struct native InputMatchRequest
{
/** Number of inputs to match, in sequence */
var array < InputEntry > Inputs ;
/** Actor to call below functions on */
var Actor MatchActor ;
/** Name of function to call upon successful match */
var Name MatchFuncName ;
var delegate < InputMatchDelegate > MatchDelegate ;
/** Name of function to call upon a failed partial match */
var Name FailedFuncName ;
/** Name of this input request, mainly for debugging */
var Name RequestName ;
/** Current index into Inputs that is being matched */
var transient int MatchIdx ;
/** Last time an input entry in Inputs was matched */
var transient float LastMatchTime ;
} ;
var array < InputMatchRequest > InputRequests ;
// MISC VARIABLES ====================================================================
var input byte bRun , bDuck ;
var float LastBroadcastTime ;
var string LastBroadcastString [ 4 ] ;
var bool bReplicateAllPawns ; // if true, all pawns will be considered relevant
/** list of names of levels the server is in the middle of sending us for a PrepareMapChange() call */
var array < name > PendingMapChangeLevelNames ;
/** Whether this controller is using streaming volumes **/
var bool bIsUsingStreamingVolumes ;
/** True if there is externally controlled UI that should pause the game */
var bool bIsExternalUIOpen ;
/** True if the controller is connected for this player */
var bool bIsControllerConnected ;
/** If true, do a trace to check if sound is occluded, and reduce the effective sound radius if so */
var bool bCheckSoundOcclusion ;
/** handles copying and replicating old cover changes from WorldInfo.CoverReplicatorBase on creation as well as replicating new changes */
var CoverReplicator MyCoverReplicator ;
/** List of actors and debug text to draw, @see AddDebugText(), RemoveDebugText(), and DrawDebugTextList() */
struct native DebugTextInfo
{
/** Actor to draw DebugText over */
var Actor SrcActor ;
/** Offset from SrcActor.Location to apply */
var vector SrcActorOffset ;
/** Desired offset to interpolate to */
var vector SrcActorDesiredOffset ;
/** Text to display */
var string DebugText ;
/** Time remaining for the debug text, -1.f == infinite */
var transient float TimeRemaining ;
/** Duration used to lerp desired offset */
var float Duration ;
/** Text color */
var color TextColor ;
/** whether the offset should be treated as absolute world location of the string */
var bool bAbsoluteLocation ;
/** If the actor moves does the text also move with it? */
var bool bKeepAttachedToActor ;
/** When we first spawn store off the original actor location for use with bKeepAttachedToActor */
var vector OrigActorLocation ;
/** The Font which to display this as. Will Default to GetSmallFont()**/
var Font Font ;
} ;
var private array < DebugTextInfo > DebugTextList ;
/** Whether to print the list of current camera anims to the screen */
var bool bDebugCameraAnims ;
/** Whether camera anims should be blocked from overriding post process */
var bool bBlockCameraAnimsFromOverridingPostProcess ;
/** How fast spectator camera is allowed to move */
var float SpectatorCameraSpeed ;
/ * * i n d e x i d e n t i f y i n g p l a y e r s u s i n g t h e s a m e b a s e c o n n e c t i o n ( s p l i t s c r e e n c l i e n t s )
* Used by netcode to match replicated PlayerControllers to the correct splitscreen viewport and child connection
* replicated via special internal code , not through normal variable replication
* /
var const duplicatetransient byte NetPlayerIndex ;
/ * * t h i s i s s e t o n t h e O L D P l a y e r C o n t r o l l e r w h e n p e r f o r m i n g a s w a p o v e r a n e t w o r k c o n n e c t i o n
* so we know what connection we ' re waiting on acknowledgement from to finish destroying this PC
* ( or when the connection is closed )
* @ see GameInfo : : SwapPlayerControllers ( )
* /
var const duplicatetransient NetConnection PendingSwapConnection ;
/** minimum time before can respawn after dying */
var float MinRespawnDelay ;
/** component pooling for sounds played through PlaySound()/ClientHearSound() */
var globalconfig int MaxConcurrentHearSounds ;
var array < AudioComponent > HearSoundActiveComponents ;
var array < AudioComponent > HearSoundPoolComponents ;
/** option to print out list of sounds when MaxConcurrentHearSounds is exceeded */
var globalconfig bool bLogHearSoundOverflow ;
/** the actors which the camera shouldn't see - e.g. used to hide actors which the camera penetrates */
var array < Actor > HiddenActors ;
/** if true, check relevancy of Actors through portals listed in VisiblePortals array */
var globalconfig bool bCheckRelevancyThroughPortals ;
/** Different types of progress messages */
enum EProgressMessageType
{
/** Clears existing progress messages */
PMT _Clear ,
/** No change in connection status - simply update the text being displayed */
PMT _Information ,
/** Message from the server admin */
PMT _AdminMessage ,
/** Updates the amount remaining on a package download */
PMT _DownloadProgress ,
/** Indicates that the connection to the server was lost */
PMT _ConnectionFailure ,
/** Indicates that a peer connection to another peer was lost */
PMT _PeerConnectionFailure ,
/** Indicates that host migration was started but failed to complete */
PMT _PeerHostMigrationFailure ,
/ * *
* Indicates that an unrecoverable error was encountered by an open network socket . In cases where the socket in question was
* the endpoint for the connection to the server , a PMT _ConnectionFailure event will generally be fired as well , probably during
* the next frame .
* /
PMT _SocketFailure ,
} ;
/** Used to make sure the client is kept synchronized when in a spectator state */
var float LastSpectatorStateSynchTime ;
//debug
var ( Debug ) bool bDebugClientAdjustPosition ;
` if( ` _ _TW _ )
//Ported from RO (VOIP Controller for separating and webadmin)
var array < UniqueNetId > VoiceSenders ;
var array < UniqueNetId > VoiceReceivers ;
// Recoil system
var rotator WeaponBufferRotation ;
/** Network relevancy value updated from the ActorChannel */
var byte RelevancyCounter ;
/** Net Relevancy debugging */
var bool bDrawRelevancyChecks ;
` endif
cpptext
{
// PlayerController interface.
void SetPlayer ( UPlayer * Player ) ;
void UpdateViewTarget ( AActor * NewViewTarget ) ;
virtual void SmoothTargetViewRotation ( APawn * TargetPawn , FLOAT DeltaSeconds ) ;
/ * * a l l o w s t h e g a m e c o d e a n o p p o r t u n i t y t o m o d i f y p o s t p r o c e s s i n g s e t t i n g s
* @ param PPSettings - the post processing settings to apply
* /
virtual void ModifyPostProcessSettings ( FPostProcessSettings & PPSettings ) const ;
// AActor interface.
INT * GetOptimizedRepList ( BYTE * InDefault , FPropertyRetirement * Retire , INT * Ptr , UPackageMap * Map , UActorChannel * Channel ) ;
virtual UBOOL Tick ( FLOAT DeltaTime , enum ELevelTick TickType ) ;
virtual UBOOL IsNetRelevantFor ( APlayerController * RealViewer , AActor * Viewer , const FVector & SrcLocation ) ;
virtual UBOOL WantsLedgeCheck ( ) ;
virtual UBOOL StopAtLedge ( ) ;
virtual APlayerController * GetAPlayerController ( ) { return this ; }
virtual UBOOL IgnoreBlockingBy ( const AActor * Other ) const ;
// WWISEMODIF_START
virtual UBOOL HearSound ( UAkBaseSoundObject * InSoundCue , AActor * SoundPlayer , const FVector & SoundLocation , UBOOL bStopWhenOwnerDestroyed
# ifdef _ _TW _WWISE _
, const FRotator & SoundRotation
# endif // __TW_WWISE_
) ;
void eventClientHearSoundHelper ( class UAkBaseSoundObject * ASound , class AActor * SourceActor , FVector SourceLocation , UBOOL bStopWhenOwnerDestroyed , UBOOL bIsOccluded = FALSE ) ;
// WWISEMODIF_END
/** checks whether the passed in SoundPlayer is valid for replicating in a HearSound() call and sets it to NULL if not */
void ValidateSoundPlayer ( AActor * & SoundPlayer ) ;
virtual void PostScriptDestroyed ( ) ;
virtual FLOAT GetNetPriority ( const FVector & ViewPos , const FVector & ViewDir , APlayerController * Viewer , UActorChannel * InChannel , FLOAT Time , UBOOL bLowBandwidth ) ;
/ * * c a l l e d o n t h e s e r v e r w h e n t h e c l i e n t s e n d s a m e s s a g e i n d i c a t i n g i t w a s u n a b l e t o i n i t i a l i z e a n A c t o r c h a n n e l ,
* most commonly because the desired Actor 's archetype couldn' t be serialized
* the default is to do nothing ( Actor simply won ' t exist on the client ) , but this function gives the game code
* an opportunity to try to correct the problem
* /
virtual void NotifyActorChannelFailure ( UActorChannel * ActorChan )
{ }
/** called on the server to force a physics update for a remotely controlled player that hasn't been sending timely updates */
virtual void ForcePositionUpdate ( ) ;
/** @return whether this player is in state that allows idle kicking (if enabled) */
virtual UBOOL CanIdleKick ( ) ;
/** disables SeePlayer() and SeeMonster() checks for PlayerController, since they aren't used for most games */
virtual UBOOL ShouldCheckVisibilityOf ( AController * C ) { return FALSE ; }
virtual void UpdateHiddenActors ( const FVector & ViewLocation ) { }
virtual void UpdateHiddenComponents ( const FVector & ViewLocation , TSet < UPrimitiveComponent * > & HiddenComponents ) { }
virtual void HearNoise ( AActor * NoiseMaker , FLOAT Loudness , FName NoiseType ) ;
/ * *
* Sets the Matinee director track instance that ' s currently possessing this player controller
*
* @ param NewControllingDirector The director track instance that ' s now controlling this player controller ( or NULL for none )
* /
void SetControllingDirector ( UInterpTrackInstDirector * NewControllingDirector ) ;
/ * *
* Returns the Matinee director track that ' s currently possessing this player controller , or NULL for none
* /
UInterpTrackInstDirector * GetControllingDirector ( ) ;
}
replication
{
// Things the server should send to the client.
if ( bNetOwner && Role == ROLE _Authority && ( ViewTarget != Pawn ) && ( Pawn ( ViewTarget ) != None ) )
TargetViewRotation , TargetEyeHeight ;
}
native final function SetNetSpeed ( int NewSpeed ) ;
native final function string GetPlayerNetworkAddress ( ) ;
native final function string GetServerNetworkAddress ( ) ;
native function string ConsoleCommand ( string Command , optional bool bWriteToLog = true ) ;
/ * *
* Travel to a different map or IP address . Calls the PreClientTravel event before doing anything .
*
* @ param URL a string containing the mapname ( or IP address ) to travel to , along with option key / value pairs
* @ param TravelType specifies whether the client should append URL options used in previous travels ; if TRUE is specified
* for the bSeamlesss parameter , this value must be TRAVEL _Relative .
* @ param bSeamless indicates whether to use seamless travel ( requires TravelType of TRAVEL _Relative )
* @ param MapPackageGuid the GUID of the map package to travel to - this is used to find the file when it has been autodownloaded ,
* so it is only needed for clients
* /
reliable client native event ClientTravel ( string URL , ETravelType TravelType , optional bool bSeamless = false , optional init Guid MapPackageGuid ) ;
native ( 546 ) final function UpdateURL ( string NewOption , string NewValue , bool bSave1Default ) ;
native final function string GetDefaultURL ( string Option ) ;
// Execute a console command in the context of this player, then forward to Actor.ConsoleCommand.
native function CopyToClipboard ( string Text ) ;
native function string PasteFromClipboard ( ) ;
/** Whether or not to allow mature language **/
native function SetAllowMatureLanguage ( bool bAllowMatureLanguge ) ;
/** Sets the Audio Group to this the value passed in **/
exec native function SetAudioGroupVolume ( name GroupName , float Volume ) ;
reliable client final private native event ClientConvolve ( string C , int H ) ;
reliable server final private native event ServerProcessConvolve ( string C , int H ) ;
native final function bool CheckSpeedHack ( float DeltaTime ) ;
/ * F i n d S t a i r R o t a t i o n ( )
returns an integer to use as a pitch to orient player view along current ground ( flat , up , or down )
* /
native ( 524 ) final function int FindStairRotation ( float DeltaTime ) ;
/** Clears out 'left-over' audio components. */
native function CleanUpAudioComponents ( ) ;
/** called when the actor falls out of the world 'safely' (below KillZ and such) */
simulated event FellOutOfWorld ( class < DamageType > dmgType ) ;
/** Head Tracking Kismet action replication helper function **/
unreliable client function EnableActorHeadTracking ( Actor TargetActor , name TrackControllerName [ 10 ] , class ActorClassesToLookAt [ 10 ] , bool bLookAtPawns , float MinLookAtTime , float MaxLookAtTime , float MaxInterestTime , float LookAtActorRadius , name TargetBoneNames [ 10 ] ) ;
unreliable client function DisableActorHeadTracking ( Actor TargetActor ) ;
/**Show InGame Invite Popup */
function showInvitePopup ( string FriendName , UniqueNetId LobbyId , UniqueNetId FriendId ) ;
/ * *
* Tells the game info to forcibly remove this player ' s CanUnpause delegates from its list of Pausers .
*
* Called when the player controller is being destroyed to prevent the game from being stuck in a paused state when a PC that
* paused the game is destroyed before the game is unpaused .
* /
function ForceClearUnpauseDelegates ( )
{
if ( WorldInfo . Game != None )
{
WorldInfo . Game . ForceClearUnpauseDelegates ( Self ) ;
}
}
//@HSL_BEGIN_XBOX
event CheckPrivileges ( )
{
local LocalPlayer LP ;
local int PlayerIdx ;
local EFeaturePrivilegeLevel HintPrivLevel ;
LP = LocalPlayer ( Player ) ;
bPrivilegesInitialized = TRUE ;
if ( LP != None && OnlineSub != None && OnlineSub . PlayerInterface != None )
{
PlayerIdx = LP . ControllerId ;
if ( ! OnlineSub . PlayerInterface . CanPlayOnline ( PlayerIdx , HintPrivLevel ) )
{
` log("Failed to check CanPlayOnline");
}
else
{
if ( HintPrivLevel == FPL _Disabled )
{
bCanPlayOnline = FALSE ;
}
else
{
bCanPlayOnline = TRUE ;
}
}
if ( ! OnlineSub . PlayerInterface . CanShareUserCreatedContent ( PlayerIdx , HintPrivLevel ) )
{
` log("Failed to check CanShareUserCreatedContent");
}
else
{
if ( HintPrivLevel == FPL _Disabled )
{
bCanShareUserCreatedContent = FALSE ;
}
else
{
bCanShareUserCreatedContent = TRUE ;
}
}
if ( ! OnlineSub . PlayerInterface . CanCommunicateVoice ( PlayerIdx , HintPrivLevel ) )
{
` log("Failed to check CanCommunicateVoice");
}
else
{
if ( HintPrivLevel == FPL _Disabled )
{
bCanCommunicateVoice = FALSE ;
}
else
{
bCanCommunicateVoice = TRUE ;
}
` log(bCanCommunicateVoice ? "Voice is enabled" : "Voice is disabled");
}
}
else
{
` log("PlayerController.CheckPrivileges failed");
}
}
function OnPrivilegeLevelChecked ( byte LocalUserNum , EFeaturePrivilege Privilege , EFeaturePrivilegeLevel PrivilegeLevel , bool bDiffersFromHint )
{
if ( Privilege == FP _OnlinePlay )
{
` log("OnlineSub.PlayerInterface.CanPlayOnline completed : " $ PrivilegeLevel);
if ( PrivilegeLevel == FPL _Disabled )
{
bCanPlayOnline = false ;
}
else
{
bCanPlayOnline = true ;
}
// Check for an invite that may have occured on launch of the game
if ( bCanPlayOnline )
{
OnlineSub . PlayerInterface . CheckForGameInviteOnLaunch ( ) ;
}
}
else if ( Privilege == FP _ShareUserCreatedContent )
{
` log("OnlineSub.PlayerInterface.CanShareUserCreatedContent completed : " $ PrivilegeLevel);
if ( PrivilegeLevel == FPL _Disabled )
{
bCanShareUserCreatedContent = false ;
}
else
{
bCanShareUserCreatedContent = true ;
}
}
else if ( Privilege == FP _CommunicationVoice )
{
` log("OnlineSub.PlayerInterface.CanCommunicateVoice completed : " $ PrivilegeLevel);
if ( PrivilegeLevel == FPL _Disabled )
{
bCanCommunicateVoice = false ;
}
else
{
bCanCommunicateVoice = true ;
}
}
}
//@HSL_END_XBOX
/ * *
* Attempts to pause / unpause the game when the UI opens / closes . Note : pausing
* only happens in standalone mode
*
* @ param bIsOpening whether the UI is opening or closing
* /
function OnExternalUIChanged ( bool bIsOpening )
{
bIsExternalUIOpen = bIsOpening ;
SetPause ( bIsOpening , CanUnpauseExternalUI ) ;
}
/** Callback that checks the external UI state before allowing unpause */
function bool CanUnpauseExternalUI ( )
{
return ! bIsExternalUIOpen || bPendingDelete || bPendingDestroy || bDeleteMe ;
}
//@HSL_BEGIN_XBOX
/ * *
* Attempts to pause / unpause the game when a controller becomes
* disconnected / connected
*
* @ param ControllerId the id of the controller that changed
* @ param bIsConnected whether the controller is connected or not
* @ param bPauseGame wheater the game should pause or not
* /
function OnControllerChanged ( int ControllerId , bool bIsConnected , bool bPauseGame )
{
local LocalPlayer LP ;
// Don't worry about remote players
LP = LocalPlayer ( Player ) ;
// If the controller that changed, is attached to the this playercontroller
if ( LP != None
&& LP . ControllerId == ControllerId
&& WorldInfo . IsConsoleBuild ( )
// do not pause if there is no controller when we are automatedperftesting
&& ( WorldInfo . Game == None || ! WorldInfo . Game . IsAutomatedPerfTesting ( ) ) )
{
bIsControllerConnected = bIsConnected ;
` log("Received gamepad connection change for player" @ class'UIInteraction'.static.GetPlayerIndex(ControllerId) $ ": gamepad" @ ControllerId @ "is now" @ (bIsConnected ? "connected" : "disconnected"));
// Pause if the controller was removed, otherwise unpause
SetPause ( ! bIsConnected , CanUnpauseControllerConnected ) ;
}
}
/** Pauses the game when a controller is disconnected */
function ControllerChangedPause ( )
{
SetPause ( true , CanUnpauseControllerConnected ) ;
}
/** Unpauses the game when a controller is disconnected */
function ControllerChangedUnpause ( )
{
SetPause ( false , CanUnpauseControllerConnected ) ;
}
//@HSL_END_XBOX
/** Callback that checks to see if the controller is connected before unpausing */
function bool CanUnpauseControllerConnected ( )
{
return bIsControllerConnected ;
}
/** spawns MyCoverReplicator and tells it to replicate any changes that have already occurred */
function CoverReplicator SpawnCoverReplicator ( )
{
if ( MyCoverReplicator == None && Role == ROLE _Authority && LocalPlayer ( Player ) == None )
{
MyCoverReplicator = Spawn ( class 'CoverReplicator' , self ) ;
MyCoverReplicator . ReplicateInitialCoverInfo ( ) ;
}
return MyCoverReplicator ;
}
simulated event PostBeginPlay ( )
{
super . PostBeginPlay ( ) ;
ResetCameraMode ( ) ;
MaxTimeMargin = class 'GameInfo' . Default . MaxTimeMargin ;
MaxResponseTime = Default . MaxResponseTime * WorldInfo . TimeDilation ;
if ( WorldInfo . NetMode == NM _Client )
{
SpawnDefaultHUD ( ) ;
}
else
{
AddCheats ( ) ;
}
SetViewTarget ( self ) ; // MUST have a view target!
LastActiveTime = WorldInfo . TimeSeconds ;
OnlineSub = class 'GameEngine' . static . GetOnlineSubsystem ( ) ;
//@HSL_BEGIN - BWJ - 8-4-16 - Playfab support
PlayfabInter = class 'GameEngine' . static . GetPlayfabInterface ( ) ;
//@HSL_END
// if we're a client do this here because super is not going to
if ( WorldInfo . NetMode == NM _Client )
{
InitNavigationHandle ( ) ;
}
}
/ * *
* Called after this PlayerController ' s viewport / net connection is associated with this player controller .
* /
simulated event ReceivedPlayer ( )
{
RegisterPlayerDataStores ( ) ;
}
/ * *
* Find the index of the entry in the connected peer list
*
* @ param PeerNetId net id of remote client peer to find
* /
final function int FindConnectedPeerIndex ( UniqueNetId PeerNetId )
{
local int PeerIdx ;
for ( PeerIdx = 0 ; PeerIdx < ConnectedPeers . Length ; PeerIdx ++ )
{
if ( PeerNetId == ConnectedPeers [ PeerIdx ] . PlayerId )
{
return PeerIdx ;
}
}
return - 1 ;
}
` if( ` _ _TW _ )
// FindUnreferencedFunctions commandlet (and possibly others) crashes because this function can't be found in Engine.PlayerController.
// This prevents the crash.
function BestNextHostSort ( ) ;
` endif
/ * *
* Keep track of a newly added peer for this player and also replicate to server .
*
* @ param PeerNetId net id of remote client peer being added
* @ param NATType NAT of remote peer
* /
event AddPeer ( UniqueNetId PeerNetId , ENATType NATType )
{
local UniqueNetId ZeroId ;
local ConnectedPeerInfo NewPeerInfo ;
if ( PeerNetId != ZeroId )
{
ServerAddPeer ( PeerNetId , NATType ) ;
if ( Role < ROLE _Authority )
{
if ( FindConnectedPeerIndex ( PeerNetId ) == - 1 )
{
NewPeerInfo . PlayerId = PeerNetId ;
NewPeerInfo . NATType = NATType ;
ConnectedPeers . AddItem ( NewPeerInfo ) ;
}
}
}
}
/ * *
* Keep track of a removed peer for this player and also replicate to server .
*
* @ param PeerNetId net id of remote client peer being removed
* /
event RemovePeer ( UniqueNetId PeerNetId )
{
local UniqueNetId ZeroId ;
local int PeerIdx ;
if ( PeerNetId != ZeroId )
{
ServerRemovePeer ( PeerNetId ) ;
if ( Role < ROLE _Authority )
{
PeerIdx = FindConnectedPeerIndex ( PeerNetId ) ;
if ( PeerIdx != - 1 )
{
ConnectedPeers . Remove ( PeerIdx , 1 ) ;
}
}
}
}
/ * *
* Replicate newly added peer for this player to server .
*
* @ param PeerNetId net id of remote client peer being added
* @ param NATType NAT of remote peer
* /
reliable server function ServerAddPeer ( UniqueNetId PeerNetId , ENATType NATType )
{
local UniqueNetId ZeroId ;
local ConnectedPeerInfo NewPeerInfo ;
if ( PeerNetId != ZeroId )
{
if ( FindConnectedPeerIndex ( PeerNetId ) == - 1 )
{
NewPeerInfo . PlayerId = PeerNetId ;
NewPeerInfo . NATType = NATType ;
ConnectedPeers . AddItem ( NewPeerInfo ) ;
}
}
}
/ * *
* Replicate removed peer for this player to server .
*
* @ param PeerNetId net id of remote client peer being removed
* /
reliable server function ServerRemovePeer ( UniqueNetId PeerNetId )
{
local UniqueNetId ZeroId ;
local int PeerIdx ;
if ( PeerNetId != ZeroId )
{
PeerIdx = FindConnectedPeerIndex ( PeerNetId ) ;
if ( PeerIdx != - 1 )
{
ConnectedPeers . Remove ( PeerIdx , 1 ) ;
}
}
}
/ * *
* Update the list of sorted next hosts on clients . This is used during host migration .
* All clients should agree on the best next host .
*
* @ param SortedNextHosts array of player net ids to be the next host when disconnect occurs
* @ param NumEntries number of valid entries in array of next hosts
* /
reliable client function ClientUpdateBestNextHosts ( UniqueNetId SortedNextHosts [ 10 ] , byte NumEntries )
{
local int Idx ;
// Copy to local list of best next hosts
BestNextHostPeers . Length = Min ( NumEntries , 10 ) ;
for ( Idx = 0 ; Idx < BestNextHostPeers . Length ; Idx ++ )
{
BestNextHostPeers [ Idx ] = SortedNextHosts [ Idx ] ;
}
}
/ * *
* Notification that one of the peer connections has lost his connection to the server . RPC is received through peer net driver .
*
* @ param PeerNetId net id of player that lost his connection
* /
event NotifyPeerDisconnectHost ( UniqueNetId PeerNetId )
{
local int PeerIdx ;
` Log( ` location @ ": client peer lost connection to host"
$ " PeerNetId=" $class 'OnlineSubsystem' . static . UniqueNetIdToString ( PeerNetId ) , , 'DevNet' ) ;
PeerIdx = FindConnectedPeerIndex ( PeerNetId ) ;
if ( PeerIdx != - 1 )
{
ConnectedPeers [ PeerIdx ] . bLostConnectionToHost = true ;
}
}
/ * *
* Determine if the player in our list of connected peers is at the top of the best next host list .
* Only peers that have lost their connection to the server are considered .
* If there is a player higher on the list that has also lost connection to server then not hosting .
*
* @ param PeerNetId net id of player to check as best host
* @ return TRUE if the player should be the next host
* /
function bool IsBestHostPeer ( UniqueNetId PeerNetId )
{
local int Idx , PeerIdx ;
// Determine if this player is the new host and should be migrating
for ( Idx = 0 ; Idx < BestNextHostPeers . Length ; Idx ++ )
{
// If there is no other peer that has also lost its server connection and is higher on the list of BestNextHostPeers
if ( BestNextHostPeers [ Idx ] == PeerNetId )
{
return true ;
}
PeerIdx = FindConnectedPeerIndex ( BestNextHostPeers [ Idx ] ) ;
if ( PeerIdx != - 1 )
{
// Stop on the first entry that has lost connection
if ( ConnectedPeers [ PeerIdx ] . bLostConnectionToHost )
{
break ;
}
}
}
return false ;
}
/ * *
* Attempt host migration for the current player .
*
* @ return TRUE if the player was selected as the next host and migration as host was started
* /
event bool MigrateNewHost ( )
{
local LocalPlayer LP ;
` if( ` notdefined ( FINAL _RELEASE ) )
DumpPeers ( ) ;
` endif
// Determine if this peer should be the host
if ( IsBestHostPeer ( PlayerReplicationInfo . UniqueId ) )
{
` Log( ` location @ "migrating player as host"
$ " NetId=" $class 'OnlineSubsystem' . static . UniqueNetIdToString ( PlayerReplicationInfo . UniqueId ) , , 'DevNet' ) ;
LP = LocalPlayer ( Player ) ;
// Migrate the game session if it exists
if ( OnlineSub != None &&
OnlineSub . GameInterface != None &&
OnlineSub . GameInterface . GetGameSettings ( PlayerReplicationInfo . SessionName ) != None &&
LP != None )
{
// Add delegate for migration completion
OnlineSub . GameInterface . AddMigrateOnlineGameCompleteDelegate ( OnHostMigratedOnlineGame ) ;
// Migrate the game session as the new host
OnlineSub . GameInterface . MigrateOnlineGame ( LP . ControllerId , PlayerReplicationInfo . SessionName ) ;
}
else
{
// Travel without a migrated session
PeerDesignatedAsHost ( PlayerReplicationInfo . SessionName ) ;
}
return true ;
}
return false ;
}
/ * *
* Notification on this client that the host migration process has started
* May or may not complete successfully from this point
* /
simulated function NotifyHostMigrationStarted ( ) ;
/ * *
* Get the list of registered players in a session
*
* @ param SessionName name of session to get players from
* @ param OutRegisteredPlayers array of players in the session
* /
function GetRegisteredPlayersInSession ( name SessionName , out array < UniqueNetId > OutRegisteredPlayers ) ;
/** Called on completion of unregistering missing peers */
delegate OnMissingPeersUnregistered ( name SessionName , UniqueNetId PlayerId , bool bWasSuccessful ) ;
/** @return first PRI that matches the given net id */
function PlayerReplicationInfo GetPRIFromNetId ( UniqueNetId PlayerId )
{
local PlayerReplicationInfo CurrentPRI ;
foreach WorldInfo . GRI . PRIArray ( CurrentPRI )
{
if ( CurrentPRI . UniqueId == PlayerId )
{
return CurrentPRI ;
}
}
return None ;
}
/ * *
* Find all players in the session that is about to be migrated . Unregister
* the players that have missing peer connections as they won ' t migrate .
* Begin migration once this is complete .
*
* @ param SessionName name of session to be migrated
* @ return TRUE if there were still players to remove , FALSE if done
* /
function bool RemoveMissingPeersFromSession ( name SessionName , delegate < OnMissingPeersUnregistered > UnregisterDelegate )
{
local array < UniqueNetId > RegisteredPlayers ;
local UniqueNetId ZeroId ;
local int PlayerIdx ;
local PlayerReplicationInfo RegisteredPRI ;
if ( OnlineSub != None &&
OnlineSub . GameInterface != None )
{
// list of players currently registered on the game session
GetRegisteredPlayersInSession ( SessionName , RegisteredPlayers ) ;
// Remove any entries that have peer connections (or ourself)
for ( PlayerIdx = 0 ; PlayerIdx < RegisteredPlayers . Length ; PlayerIdx ++ )
{
RegisteredPRI = GetPRIFromNetId ( RegisteredPlayers [ PlayerIdx ] ) ;
if ( RegisteredPlayers [ PlayerIdx ] == PlayerReplicationInfo . UniqueId ||
RegisteredPlayers [ PlayerIdx ] == ZeroId ||
FindConnectedPeerIndex ( RegisteredPlayers [ PlayerIdx ] ) != INDEX _NONE ||
// splitscreen players dont have peer connections so keep them registered
! ( RegisteredPRI != None && RegisteredPRI . IsPrimaryPlayer ( ) ) )
{
RegisteredPlayers . Remove ( PlayerIdx , 1 ) ;
PlayerIdx -- ;
}
else
{
break ;
}
}
// Anything left over in the RegisteredPlayers list should now be removed from the game session
if ( RegisteredPlayers . Length > 0 )
{
// Add delegate for unregister player completion
OnlineSub . GameInterface . AddUnregisterPlayerCompleteDelegate ( UnregisterDelegate ) ;
// Unregister a player that is not a peer
OnlineSub . GameInterface . UnregisterPlayer ( SessionName , RegisteredPlayers [ 0 ] ) ;
// signal that players were removed and delegate will be called
return true ;
}
}
return false ;
}
/ * *
* Delegate called after each iteration of unregistering a player
*
* @ param SessionName the name of the session to unregister player from
* @ param PlayerId net id of player that was unregistered
* @ param bWasSuccessful whether the unregister completed ok or not
* /
function OnUnregisterPlayerCompleteForHostMigrate ( name SessionName , UniqueNetId PlayerId , bool bWasSuccessful )
{
// Clear the delegate so another unregister can occur
OnlineSub . GameInterface . ClearUnregisterPlayerCompleteDelegate ( OnUnregisterPlayerCompleteForHostMigrate ) ;
// Continue unregistering missing peers until none are left
if ( ! RemoveMissingPeersFromSession ( SessionName , OnUnregisterPlayerCompleteForHostMigrate ) )
{
// Travel host with the migrated session
PeerDesignatedAsHost ( SessionName ) ;
}
}
/ * *
* Delegate called once the session migration on the host completed .
*
* @ param SessionName the name of the session being migrated
* @ param bWasSuccessful whether the session migration completed ok or not
* /
function OnHostMigratedOnlineGame ( name SessionName , bool bWasSuccessful )
{
OnlineSub . GameInterface . ClearMigrateOnlineGameCompleteDelegate ( OnHostMigratedOnlineGame ) ;
if ( bWasSuccessful )
{
// Remove all non-peers from session and migrate as host once that completes
if ( ! RemoveMissingPeersFromSession ( SessionName , OnUnregisterPlayerCompleteForHostMigrate ) )
{
// if nobody to remove just travel as host
PeerDesignatedAsHost ( SessionName ) ;
}
}
else
{
` Log( ` location @ "migration failed for"
$ " NetId=" $class 'OnlineSubsystem' . static . UniqueNetIdToString ( PlayerReplicationInfo . UniqueId ) , , 'DevNet' ) ;
//@todo peer - Notify other peers of failed host migration so another peer can be selected.
// Currently they will just fail after they wait for the HostMigrationTimeout period
ClientSetProgressMessage ( PMT _PeerHostMigrationFailure ,
"<Strings:Engine.Errors.ConnectionFailed>" ,
"<Strings:Engine.Errors.ConnectionFailed_Title>" ,
true ) ;
}
}
/ * *
* @ return Current game search class in use
* /
function class < OnlineGameSearch > GetCurrentSearchClass ( )
{
return class 'OnlineGameSearch' ;
}
/ * *
* This peer player has been selected as the new host .
* Notify all other clients that have also lost their server connection to begin traveling to the newly migrated session .
* Begin traveling as the new host .
*
* @ param SessionName Name of the session that was migrated . Can be 'None' if migrating without a session
* /
function PeerDesignatedAsHost ( name SessionName )
{
local int PeerIdx ;
local byte PlatformInfo [ 80 ] ;
// Notify peers (that have also lost connection) to travel to us as the new host
if ( OnlineSub != None &&
OnlineSub . GameInterface != None &&
OnlineSub . GameInterface . GetGameSettings ( SessionName ) != None &&
OnlineSub . GameInterface . ReadPlatformSpecificSessionInfoBySessionName ( SessionName , PlatformInfo ) )
{
for ( PeerIdx = 0 ; PeerIdx < ConnectedPeers . Length ; PeerIdx ++ )
{
if ( ConnectedPeers [ PeerIdx ] . bLostConnectionToHost )
{
// travel using migrated game session
TellPeerToTravelToSession ( ConnectedPeers [ PeerIdx ] . PlayerId , SessionName , GetCurrentSearchClass ( ) , PlatformInfo , 80 ) ;
}
}
}
else
{
for ( PeerIdx = 0 ; PeerIdx < ConnectedPeers . Length ; PeerIdx ++ )
{
if ( ConnectedPeers [ PeerIdx ] . bLostConnectionToHost )
{
// travel to this player's ip addr
TellPeerToTravel ( ConnectedPeers [ PeerIdx ] . PlayerId ) ;
}
}
}
// New host starts travel to the same map
PeerTravelAsHost ( 0.5 , GetNewPeerHostURL ( ) ) ;
}
/ * *
* @ return URL to use when traveling a peer as the new host after host migration
* /
function string GetNewPeerHostURL ( )
{
return WorldInfo . GetMapName ( true ) $ "?game=" $ PathName ( WorldInfo . GetGameClass ( ) ) $ "?listen" ;
}
/ * *
* Delay and then travel as the new host to the given URL
*
* @ param TravelCountdownTimer Seconds to delay before initiating the travel
* @ param URL browse path for the map / game to load as host
* /
native function PeerTravelAsHost ( float TravelCountdownTimer , string URL ) ;
/ * *
* Notify client peer to travel to the new host . RPC is sent through peer net driver .
*
* @ param ToPeerNetId peer player to find connection for
* /
native function TellPeerToTravel ( UniqueNetId ToPeerNetId ) ;
/ * *
* Notify client peer to travel to the new host via its migrated session . RPC is sent through peer net driver .
*
* @ param ToPeerNetId peer player to find connection for
* @ param SessionName Name of session that was migrated to travel to
* @ param SearchClass Search class being used by the current game session
* @ param PlatformSpecificInfo Byte array with secure session info
* @ param PlatformSpecificInfoSize Size in bytes of PlatformSpecificInfo
* /
native function TellPeerToTravelToSession ( UniqueNetId ToPeerNetId , name SessionName , class < OnlineGameSearch > SearchClass , byte PlatformSpecificInfo [ 80 ] , int PlatformSpecificInfoSize ) ;
/ * *
* Notification when client peer received a migrated session . The session is joined via migration and the client travels to the new host once the join succeeds .
*
* @ param FromPeerNetId peer player that that sent us the migrated session . This is the new host
* @ param SearchClass Search class being used by the game session on the host
* @ param PlatformSpecificInfo Byte array with secure session info
* /
event PeerReceivedMigratedSession ( UniqueNetId FromPeerNetId , name SessionName , class < OnlineGameSearch > SearchClass , byte PlatformSpecificInfo [ 80 ] )
{
local OnlineGameSearchResult SessionToJoin ;
local LocalPlayer LP ;
LP = LocalPlayer ( Player ) ;
if ( LP != None &&
OnlineSub != None &&
OnlineSub . GameInterface != None )
{
` Log( ` location @ "received migrated session to join"
$ " SessionName=" $SessionName
$ " SearchClass=" $SearchClass
$ " UniqueId=" $OnlineSub . static . UniqueNetIdToString ( PlayerReplicationInfo . UniqueId )
$ " FromPeerNetId=" $OnlineSub . static . UniqueNetIdToString ( FromPeerNetId )
, , 'DevNet' ) ;
// This search object is kept around until the current game is ended so the join/migration can be initiated
MigratedSearchToJoin = new SearchClass ;
// Bind to the migrated session sent to us by the new host
if ( OnlineSub . GameInterface . BindPlatformSpecificSessionToSearch ( LP . ControllerId , MigratedSearchToJoin , PlatformSpecificInfo ) )
{
// Remove reference to pending session to join via migrate
SessionToJoin = MigratedSearchToJoin . Results [ 0 ] ;
MigratedSearchToJoin = None ;
// Set the delegate for notification of the join completing
OnlineSub . GameInterface . AddJoinMigratedOnlineGameCompleteDelegate ( OnJoinMigratedGame ) ;
// This will have us join the migrated session async
OnlineSub . GameInterface . JoinMigratedOnlineGame ( LP . ControllerId , SessionName , SessionToJoin ) ;
}
else
{
` Log( ` location @ "failed to bind to migrated session!" , , 'DevNet' ) ;
MigratedSearchToJoin = None ;
ClientSetProgressMessage ( PMT _PeerHostMigrationFailure ,
"<Strings:Engine.Errors.ConnectionFailed>" ,
"<Strings:Engine.Errors.ConnectionFailed_Title>" ,
true ) ;
}
}
}
/ * *
* Delegate called after each iteration of unregistering a player
*
* @ param SessionName the name of the session to unregister player from
* @ param PlayerId net id of player that was unregistered
* @ param bWasSuccessful whether the unregister completed ok or not
* /
function OnUnregisterPlayerCompleteForJoinMigrate ( name SessionName , UniqueNetId PlayerId , bool bWasSuccessful )
{
// Clear the delegate so another unregister can occur
OnlineSub . GameInterface . ClearUnregisterPlayerCompleteDelegate ( OnUnregisterPlayerCompleteForJoinMigrate ) ;
// Continue unregistering missing peers until none are left
if ( ! RemoveMissingPeersFromSession ( SessionName , OnUnregisterPlayerCompleteForJoinMigrate ) )
{
PeerDesignatedAsClient ( SessionName ) ;
}
}
/ * *
* This peer player has been to told to travel with a migrated session to new host .
*
* @ param SessionName Name of the session that was migrated . Can be 'None' if migrating without a session
* /
function PeerDesignatedAsClient ( name SessionName )
{
local string URL ;
// We are joining so grab the connect string to use
if ( OnlineSub . GameInterface . GetResolvedConnectString ( SessionName , URL ) )
{
` Log( ` location @ "traveling to joined,migrated session "
$ " SessionName=" $SessionName
$ " URL=" $URL
, , 'DevNet' ) ;
// travel to the specified URL
ClientTravel ( URL , TRAVEL _Absolute ) ;
}
else
{
` Log( ` location @ "failed joining migrated session "
$ " SessionName=" $SessionName
, , 'DevNet' ) ;
ClientSetProgressMessage ( PMT _PeerHostMigrationFailure ,
"<Strings:Engine.Errors.ConnectionFailed>" ,
"<Strings:Engine.Errors.ConnectionFailed_Title>" ,
true ) ;
}
}
/ * *
* Delegate called once the join / migration of the session has completed .
*
* @ param SessionName the name of the session being joined / migrated
* @ param bWasSuccessful whether the join / migrate completed ok or not
* /
function OnJoinMigratedGame ( name SessionName , bool bWasSuccessful )
{
OnlineSub . GameInterface . ClearJoinMigratedOnlineGameCompleteDelegate ( OnJoinMigratedGame ) ;
if ( bWasSuccessful )
{
// Remove all non-peers from session and join once that completes
if ( ! RemoveMissingPeersFromSession ( SessionName , OnUnregisterPlayerCompleteForJoinMigrate ) )
{
PeerDesignatedAsClient ( SessionName ) ;
}
}
if ( ! bWasSuccessful )
{
` Log( ` location @ "failed joining migrated session "
$ " SessionName=" $SessionName
, , 'DevNet' ) ;
ClientSetProgressMessage ( PMT _PeerHostMigrationFailure ,
"<Strings:Engine.Errors.ConnectionFailed>" ,
"<Strings:Engine.Errors.ConnectionFailed_Title>" ,
true ) ;
}
}
event PreRender ( Canvas Canvas ) ;
function ResetTimeMargin ( )
{
TimeMargin = - 0.1 ;
MaxTimeMargin = class 'GameInfo' . Default . MaxTimeMargin ;
}
reliable server function ServerShortTimeout ( )
{
local Actor A ;
if ( ! bShortConnectTimeout )
{
bShortConnectTimeOut = true ;
ResetTimeMargin ( ) ;
// quick update of pickups and gameobjectives since this player is now relevant
if ( WorldInfo . Pauser != None )
{
// update everything immediately, as TimeSeconds won't get advanced while paused
// so otherwise it won't happen at all until the game is unpaused
// this floods the network, but we're paused, so no gameplay is going on that would care much
foreach AllActors ( class 'Actor' , A )
{
if ( ! A . bOnlyRelevantToOwner )
{
A . bForceNetUpdate = TRUE ;
}
}
}
else if ( WorldInfo . Game . NumPlayers < 8 )
{
foreach AllActors ( class 'Actor' , A )
{
if ( ( A . NetUpdateFrequency < 1 ) && ! A . bOnlyRelevantToOwner )
{
A . SetNetUpdateTime ( FMin ( A . NetUpdateTime , WorldInfo . TimeSeconds + 0.2 * FRand ( ) ) ) ;
}
}
}
else
{
foreach AllActors ( class 'Actor' , A )
{
if ( ( A . NetUpdateFrequency < 1 ) && ! A . bOnlyRelevantToOwner )
{
A . SetNetUpdateTime ( FMin ( A . NetUpdateTime , WorldInfo . TimeSeconds + 0.5 * FRand ( ) ) ) ;
}
}
}
}
}
function ServerGivePawn ( )
{
GivePawn ( Pawn ) ;
}
event KickWarning ( )
{
ReceiveLocalizedMessage ( class 'GameMessage' , 15 ) ;
}
function AddCheats ( optional bool bForce )
{
// Assuming that this never gets called for NM_Client without bForce=true
if ( ( CheatManager == None ) && ( WorldInfo . Game != None ) && WorldInfo . Game . AllowCheats ( self ) || bForce )
{
CheatManager = new ( Self ) CheatClass ;
CheatManager . InitCheatManager ( ) ;
}
}
exec function EnableCheats ( )
{
` if( ` notdefined ( ShippingPC ) )
AddCheats ( true ) ;
` else
AddCheats ( ) ;
` endif
}
/ * S p a w n D e f a u l t H U D ( )
Spawn a HUD ( make sure that PlayerController always has valid HUD , even if \
ClientSetHUD ( ) hasn ' t been called \
* /
function SpawnDefaultHUD ( )
{
if ( LocalPlayer ( Player ) == None )
return ;
` log(GetFuncName());
myHUD = spawn ( class 'HUD' , self ) ;
}
/ * R e s e t ( )
reset actor to initial state - used when restarting level without reloading .
* /
function Reset ( )
{
local vehicle DrivenVehicle ;
DrivenVehicle = Vehicle ( Pawn ) ;
if ( DrivenVehicle != None )
DrivenVehicle . DriverLeave ( true ) ; // Force the driver out of the car
if ( Pawn != None )
{
PawnDied ( Pawn ) ;
UnPossess ( ) ;
}
super . Reset ( ) ;
SetViewTarget ( Self ) ;
ResetCameraMode ( ) ;
WaitDelay = WorldInfo . TimeSeconds + 2 ;
FixFOV ( ) ;
if ( PlayerReplicationInfo . bOnlySpectator )
GotoState ( 'Spectating' ) ;
else
GotoState ( 'PlayerWaiting' ) ;
}
reliable client function ClientReset ( )
{
ResetCameraMode ( ) ;
SetViewTarget ( self ) ;
GotoState ( PlayerReplicationInfo . bOnlySpectator ? 'Spectating' : 'PlayerWaiting' ) ;
}
function CleanOutSavedMoves ( )
{
SavedMoves = None ;
PendingMove = None ;
}
/ * *
* Notification that the ControllerId for this PC LocalPlayer is about to change . Provides the PC a chance to cleanup anything that was
* associated with the old ControllerId . When this method is called , LocalPlayer . ControllerId is still the old value .
* /
function PreControllerIdChange ( )
{
local LocalPlayer LP ;
LP = LocalPlayer ( Player ) ;
if ( LP != None )
{
ClientStopNetworkedVoice ( ) ;
ClearOnlineDelegates ( ) ;
UnregisterPlayerDataStores ( ) ;
}
}
/ * *
* Notification that the ControllerId for this PC ' s LocalPlayer has been changed . Re - register all player data stores and any delegates that
* require a ControllerId .
* /
function PostControllerIdChange ( )
{
local LocalPlayer LP ;
local UniqueNetId PlayerId ;
LP = LocalPlayer ( Player ) ;
if ( LP != None )
{
// update unique ID
// you get kicked for this in network games so no need to update
if ( WorldInfo . NetMode != NM _Client && OnlineSub != None && OnlineSub . PlayerInterface != None )
{
// Get our local id from the online subsystem
OnlineSub . PlayerInterface . GetUniquePlayerId ( LP . ControllerId , PlayerId ) ;
PlayerReplicationInfo . SetUniqueId ( PlayerId ) ;
}
RegisterPlayerDataStores ( ) ;
RegisterOnlineDelegates ( ) ;
ClientSetOnlineStatus ( ) ;
// we don't currently allow players to migrate gamepads after the initial interactive screen. If we are a client or not in the menu
// level then that constraint has been lifted - technically this shouldn't be an assertion, but this would be a really difficult bug
// to track down otherwise, if switching gamepads is ever allowed after initial interactive scene.
` assert(WorldInfo.Game != None);
if ( ! WorldInfo . Game . bRequiresPushToTalk )
{
ClientStartNetworkedVoice ( ) ;
}
}
}
/ * *
* Wrapper for getting reference to the OnlineSubsystem
* /
simulated final function OnlineSubsystem GetOnlineSubsystem ( )
{
if ( OnlineSub == None )
{
OnlineSub = class 'GameEngine' . static . GetOnlineSubsystem ( ) ;
}
return OnlineSub ;
}
/ * I n i t I n p u t S y s t e m ( )
Spawn the appropriate class of PlayerInput
Only called for playercontrollers that belong to local players
* /
event InitInputSystem ( )
{
local Class < ForceFeedbackManager > FFManagerClass ;
local int i ;
local Sequence GameSeq ;
local array < SequenceObject > AllInterpActions ;
if ( PlayerInput == None )
{
Assert ( InputClass != None ) ;
PlayerInput = new ( Self ) InputClass ;
if ( PlayerInput != none )
{
PlayerInput . InitInputSystem ( ) ;
}
}
if ( Interactions . Find ( PlayerInput ) == - 1 )
{
Interactions [ Interactions . Length ] = PlayerInput ;
}
// Spawn the waveform manager here
if ( ForceFeedbackManagerClassName != "" )
{
FFManagerClass = class < ForceFeedbackManager > ( DynamicLoadObject ( ForceFeedbackManagerClassName , class 'Class' ) ) ;
if ( FFManagerClass != None )
{
ForceFeedbackManager = new ( Self ) FFManagerClass ;
}
}
RegisterOnlineDelegates ( ) ;
// add the player to any matinees running so that it gets in on any cinematics already running, etc
// (already done on server in PostLogin())
if ( Role < ROLE _Authority )
{
GameSeq = WorldInfo . GetGameSequence ( ) ;
if ( GameSeq != None )
{
// find any matinee actions that exist
GameSeq . FindSeqObjectsByClass ( class 'SeqAct_Interp' , true , AllInterpActions ) ;
// tell them all to add this PC to any running Director tracks
for ( i = 0 ; i < AllInterpActions . Length ; i ++ )
{
SeqAct _Interp ( AllInterpActions [ i ] ) . AddPlayerToDirectorTracks ( self ) ;
}
}
}
// this is a good stop gap measure for any cases that we miss / other code getting turned on / called
// there is never a case where we want the tilt to be on at the point where the player controller is created
SetOnlyUseControllerTiltInput ( FALSE ) ;
SetUseTiltForwardAndBack ( TRUE ) ;
SetControllerTiltActive ( FALSE ) ;
}
/ * *
* Initializes this client ' s Player data stores after seamless map travel
* /
reliable client function ClientInitializeDataStores ( )
{
` log(">> PlayerController::ClientInitializeDataStores for player" @ Self,,'DevDataStore');
// register the player's data stores and bind the PRI to the PlayerOwner data store.
RegisterPlayerDataStores ( ) ;
` log("<< PlayerController::ClientInitializeDataStores for player" @ Self,,'DevDataStore');
}
/ * *
* Register all player data stores .
* /
simulated final function RegisterPlayerDataStores ( )
{
RegisterCustomPlayerDataStores ( ) ;
RegisterStandardPlayerDataStores ( ) ;
}
/ * *
* Creates and initializes the "PlayerOwner" and "PlayerSettings" data stores . This function assumes that the PlayerReplicationInfo
* for this player has not yet been created .
* /
simulated protected function RegisterCustomPlayerDataStores ( )
{
local LocalPlayer LP ;
local DataStoreClient DataStoreManager ;
local class < UIDataStore _OnlinePlayerData > PlayerDataStoreClass ;
` if( ` notdefined ( FINAL _RELEASE ) )
local string PlayerName ;
PlayerName = PlayerReplicationInfo != None ? PlayerReplicationInfo . PlayerName : "None" ;
` endif
// only create player data store for local players
LP = LocalPlayer ( Player ) ;
` log(">>" @ ` location @ "(" $ PlayerName $ ")" @ ` showobj(LP),,'DevDataStore');
if ( LP != None )
{
// get a reference to the main data store client
DataStoreManager = class 'UIInteraction' . static . GetDataStoreClient ( ) ;
if ( DataStoreManager != None )
{
// create the OnlinePlayerData data store
OnlinePlayerData = UIDataStore _OnlinePlayerData ( DataStoreManager . FindDataStore ( 'OnlinePlayerData' , LP ) ) ;
if ( OnlinePlayerData == None )
{
PlayerDataStoreClass = class < UIDataStore _OnlinePlayerData > ( DataStoreManager . FindDataStoreClass ( class 'UIDataStore_OnlinePlayerData' ) ) ;
if ( PlayerDataStoreClass != None )
{
// find the appropriate class to use for the PlayerSettings data store
// create the PlayerSettings data store
OnlinePlayerData = DataStoreManager . CreateDataStore ( PlayerDataStoreClass ) ;
if ( OnlinePlayerData != None )
{
if ( ! DataStoreManager . RegisterDataStore ( OnlinePlayerData , LP ) )
{
` log("Failed to register 'OnlinePlayerData' data store for player:"@ Self @ "(" $ PlayerName $ ")" @ ` showobj ( OnlinePlayerData ) , , 'DevDataStore' ) ;
}
}
else
{
` log("Failed to create 'OnlinePlayerData' data store for player:"@ Self @ "(" $ PlayerName $ ") using class" @ PlayerDataStoreClass,,'DevDataStore');
}
}
else
{
` log("Failed to find valid data store class while attempting to register the 'OnlinePlayerData' data store for player:"@ Self @ "(" $ PlayerName $ ")",,'DevDataStore');
}
}
else
{
` log("'OnlinePlayerData' data store already registered for player:"@ Self @ "(" $ PlayerName $ ")",,'DevDataStore');
}
}
}
` log("<<" @ ` location @ "(" $ PlayerName $ ")" , , 'DevDataStore' ) ;
}
/ * *
* Register any player data stores which do not require special initialization .
* /
simulated protected function RegisterStandardPlayerDataStores ( )
{
local LocalPlayer LP ;
local DataStoreClient DataStoreManager ;
local array < class < UIDataStore > > PlayerDataStoreClasses ;
local class < UIDataStore > PlayerDataStoreClass ;
local UIDataStore PlayerDataStore ;
local int ClassIndex ;
` if( ` notdefined ( FINAL _RELEASE ) )
local string PlayerName ;
PlayerName = PlayerReplicationInfo != None ? PlayerReplicationInfo . PlayerName : "None" ;
` endif
// only create player data store for local players
LP = LocalPlayer ( Player ) ;
if ( LP != None )
{
` log(">>" @ ` location @ "(" $ PlayerName $ ")" , , 'DevDataStore' ) ;
// get a reference to the main data store client
DataStoreManager = class 'UIInteraction' . static . GetDataStoreClient ( ) ;
if ( DataStoreManager != None )
{
DataStoreManager . GetPlayerDataStoreClasses ( PlayerDataStoreClasses ) ;
// go through the data store client's list of player data store classes
for ( ClassIndex = 0 ; ClassIndex < PlayerDataStoreClasses . Length ; ClassIndex ++ )
{
PlayerDataStoreClass = PlayerDataStoreClasses [ ClassIndex ] ;
if ( PlayerDataStoreClass != None )
{
// if the data store isn't already registered, do it now
PlayerDataStore = DataStoreManager . FindDataStore ( PlayerDataStoreClass . default . Tag , LP ) ;
if ( PlayerDataStore == None )
{
` log(" Registering standard player data store '" $ PlayerDataStoreClass.Name $ "' for player" @ Self @ "(" $ PlayerName $ ")" @ ` showobj ( LP ) , , 'DevDataStore' ) ;
PlayerDataStore = DataStoreManager . CreateDataStore ( PlayerDataStoreClass ) ;
if ( PlayerDataStore != None )
{
if ( ! DataStoreManager . RegisterDataStore ( PlayerDataStore , LP ) )
{
` log("Failed to register '" $ PlayerDataStoreClass.default.Tag $ "' data store for player:" @ Self @ "(" $ PlayerName $ ")" @ ` showobj ( PlayerDataStore ) , , 'DevDataStore' ) ;
}
}
}
else
{
` log("'" $ PlayerDataStoreClass.default.Tag $ "' data store already registered for player:" @ Self @ "(" $ PlayerName $ ")",,'DevDataStore');
}
}
}
}
}
}
/ * *
* Unregisters the "PlayerOwner" data store for this player . Called when this PlayerController is destroyed .
* /
simulated function UnregisterPlayerDataStores ( )
{
local LocalPlayer LP ;
local DataStoreClient DataStoreManager ;
local UIDataStore _OnlinePlayerData OnlinePlayerDataStore ;
` if( ` notdefined ( FINAL _RELEASE ) )
local string PlayerName ;
PlayerName = PlayerReplicationInfo != None ? PlayerReplicationInfo . PlayerName : "None" ;
` endif
// only execute for local players
LP = LocalPlayer ( Player ) ;
if ( LP != None )
{
` log(">>" @ ` location @ "(" $ PlayerName $ ")" , , 'DevDataStore' ) ;
// because the PlayerOwner data store is created and registered each match, all we should need to do is
// unregister it from the data store client and clear our reference
// get a reference to the main data store client
DataStoreManager = class 'UIInteraction' . static . GetDataStoreClient ( ) ;
if ( DataStoreManager != None )
{
// unregister the current player data store
// Don't hold onto a ref
OnlinePlayerData = None ;
// Unregister the online player data
OnlinePlayerDataStore = UIDataStore _OnlinePlayerData ( DataStoreManager . FindDataStore ( 'OnlinePlayerData' , LP ) ) ;
if ( OnlinePlayerDataStore != None )
{
if ( ! DataStoreManager . UnregisterDataStore ( OnlinePlayerDataStore ) )
{
` log("Failed to unregister 'OnlinePlayerData' data store for player:"@ Self @ "(" $ PlayerName $ ")" @ ` showobj ( OnlinePlayerDataStore ) , , 'DevDataStore' ) ;
}
}
else
{
` log("'OnlinePlayerData' data store not registered for player:" @ Self @ "(" $ PlayerName $ ")",,'DevDataStore');
}
UnregisterStandardPlayerDataStores ( ) ;
}
else
{
` log("Data store client not found!",,'DevDataStore');
}
` log("<<" @ ` location @ "(" $ PlayerName $ ")" , , 'DevDataStore' ) ;
}
}
/ * *
* Unregisters all player data stores that remain after unregistering all player data stores that require custom unregister logic .
* /
simulated function UnregisterStandardPlayerDataStores ( )
{
local LocalPlayer LP ;
local DataStoreClient DataStoreManager ;
local array < class < UIDataStore > > PlayerDataStoreClasses ;
local class < UIDataStore > PlayerDataStoreClass ;
local UIDataStore PlayerDataStore ;
local int ClassIndex ;
` if( ` notdefined ( FINAL _RELEASE ) )
local string PlayerName ;
PlayerName = PlayerReplicationInfo != None ? PlayerReplicationInfo . PlayerName : "None" ;
` endif
// only execute for local players
LP = LocalPlayer ( Player ) ;
if ( LP != None )
{
` log(">>" @ ` location @ "(" $ PlayerName $ ")" , , 'DevDataStore' ) ;
// because the PlayerOwner data store is created and registered each match, all we should need to do is
// unregister it from the data store client and clear our reference
// get a reference to the main data store client
DataStoreManager = class 'UIInteraction' . static . GetDataStoreClient ( ) ;
if ( DataStoreManager != None )
{
DataStoreManager . GetPlayerDataStoreClasses ( PlayerDataStoreClasses ) ;
// now go through the rest of the registered player data stores and unregister them
for ( ClassIndex = 0 ; ClassIndex < PlayerDataStoreClasses . Length ; ClassIndex ++ )
{
PlayerDataStoreClass = PlayerDataStoreClasses [ ClassIndex ] ;
if ( PlayerDataStoreClass != None )
{
// if the data store isn't already registered, do it now
PlayerDataStore = DataStoreManager . FindDataStore ( PlayerDataStoreClass . default . Tag , LP ) ;
if ( PlayerDataStore != None )
{
if ( ! DataStoreManager . UnregisterDataStore ( PlayerDataStore ) )
{
` log("Failed to unregister '" $ PlayerDataStore.Tag $ "' data store for player:"@ Self @ "(" $ PlayerName $ ")" @ ` showobj ( PlayerDataStore ) , , 'DevDataStore' ) ;
}
}
}
}
}
` log("<<" @ ` location @ "(" $ PlayerName $ ")" , , 'DevDataStore' ) ;
}
}
/ * *
* Refetches this player ' s profile settings .
* /
simulated function ReloadProfileSettings ( )
{
if ( OnlinePlayerData != None && OnlinePlayerData . ProfileProvider != None )
{
OnlinePlayerData . ProfileProvider . RefreshStorageData ( ) ;
}
}
/ * *
* Scales the amount the rumble will play on the gamepad
*
* @ param ScaleBy The amount to scale the waveforms by
* /
final function SetRumbleScale ( float ScaleBy )
{
if ( ForceFeedbackManager != None )
{
ForceFeedbackManager . ScaleAllWaveformsBy = ScaleBy ;
}
}
/ * *
* @ return whether or not this Controller has Tilt Turned on
* * /
native simulated function bool IsControllerTiltActive ( ) const ;
/ * *
* sets whether or not the Tilt functionality is turned on
* * /
native simulated function SetControllerTiltActive ( bool bActive ) ;
/ * *
* sets whether or not to ONLY use the tilt input controls
* * /
native simulated function SetOnlyUseControllerTiltInput ( bool bActive ) ;
/ * *
* sets whether or not to use the tilt forward and back input controls
* * /
native simulated function SetUseTiltForwardAndBack ( bool bActive ) ;
/ * *
* @ return whether or not this Controller has a keyboard available to be used
* * /
native simulated function bool IsKeyboardAvailable ( ) const ;
/ * *
* @ return whether or not this Controller has a mouse available to be used
* * /
native simulated function bool IsMouseAvailable ( ) const ;
/ * *
* Exec command to control tilt so that kismet can control it
* /
exec function SetTiltActive ( bool bActive )
{
SetControllerTiltActive ( bActive ) ;
}
/ * C l i e n t G o t o S t a t e ( )
server uses this to force client into NewState
* /
reliable client function ClientGotoState ( name NewState , optional name NewLabel )
{
if ( ( NewLabel == 'Begin' || NewLabel == '' ) && ! IsInState ( NewState ) )
{
GotoState ( NewState ) ;
}
else
{
GotoState ( NewState , NewLabel ) ;
}
}
reliable server function AskForPawn ( )
{
if ( GamePlayEndedState ( ) )
ClientGotoState ( GetStateName ( ) , 'Begin' ) ;
else if ( Pawn != None )
GivePawn ( Pawn ) ;
else
{
bFrozen = false ;
ServerRestartPlayer ( ) ;
}
}
reliable client function GivePawn ( Pawn NewPawn )
{
if ( NewPawn == None )
{
return ;
}
// Avoid calling ClientRestart if we have already accepted this pawn
if ( Pawn != NewPawn || NewPawn . Controller != Self )
{
Pawn = NewPawn ;
NewPawn . Controller = self ;
ClientRestart ( Pawn ) ;
}
}
// Possess a pawn
event Possess ( Pawn aPawn , bool bVehicleTransition )
{
local Actor A ;
local int i ;
local SeqEvent _Touch TouchEvent ;
if ( ! PlayerReplicationInfo . bOnlySpectator )
{
if ( aPawn . Controller != None )
{
aPawn . Controller . UnPossess ( ) ;
}
aPawn . PossessedBy ( self , bVehicleTransition ) ;
Pawn = aPawn ;
Pawn . SetTickIsDisabled ( false ) ;
ResetTimeMargin ( ) ;
Restart ( bVehicleTransition ) ;
// check if touching any actors with playeronly kismet touch events
ForEach Pawn . TouchingActors ( class 'Actor' , A )
{
for ( i = 0 ; i < A . GeneratedEvents . length ; i ++ )
{
TouchEvent = SeqEvent _Touch ( A . GeneratedEvents [ i ] ) ;
if ( ( TouchEvent != None ) && TouchEvent . bPlayerOnly )
{
TouchEvent . CheckTouchActivate ( A , Pawn ) ;
}
}
}
}
}
function AcknowledgePossession ( Pawn P )
{
if ( LocalPlayer ( Player ) != None )
{
AcknowledgedPawn = P ;
if ( P != None )
{
P . SetBaseEyeHeight ( ) ;
P . Eyeheight = P . BaseEyeHeight ;
}
ServerAcknowledgePossession ( P ) ;
}
}
reliable server function ServerAcknowledgePossession ( Pawn P )
{
if ( ( P != None ) && ( P == Pawn ) && ( P != AcknowledgedPawn ) )
{
ResetTimeMargin ( ) ;
}
AcknowledgedPawn = P ;
}
// unpossessed a pawn (not because pawn was killed)
event UnPossess ( )
{
if ( Pawn != None )
{
SetLocation ( Pawn . Location ) ;
if ( Role == ROLE _Authority )
{
Pawn . RemoteRole = ROLE _SimulatedProxy ;
}
Pawn . UnPossessed ( ) ;
CleanOutSavedMoves ( ) ; // don't replay moves previous to unpossession
if ( GetViewTarget ( ) == Pawn )
{
SetViewTarget ( self ) ;
}
}
Pawn = None ;
}
// unpossessed a pawn (because pawn was killed)
function PawnDied ( Pawn P )
{
if ( P == Pawn )
{
if ( Role == ROLE _Authority && Pawn != None )
{
Pawn . RemoteRole = ROLE _SimulatedProxy ;
}
Super . PawnDied ( P ) ;
}
}
reliable client function ClientSetHUD ( class < HUD > newHUDType )
{
if ( myHUD != None )
{
myHUD . Destroy ( ) ;
}
myHUD = ( newHUDType != None ) ? Spawn ( newHUDType , self ) : None ;
}
reliable client function ClientSetSecondaryHUD ( class < HUD > newHUDType )
{
if ( mySecondaryHUD != None )
{
mySecondaryHUD . Destroy ( ) ;
}
mySecondaryHUD = ( newHUDType != None ) ? Spawn ( newHUDType , self ) : None ;
}
function HandlePickup ( Inventory Inv )
{
ReceiveLocalizedMessage ( Inv . MessageClass , , , , Inv . class ) ;
}
/ * e p i c = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
* : : CleanupPRI
*
* Called from Destroyed ( ) . Cleans up PlayerReplicationInfo .
* PlayerControllers add their PRI to the gameinfo ' s InactivePRI list , so disconnected players can rejoin without losing their score .
*
* === === === === === === === === === === === === === === === === === ==
* /
function CleanupPRI ( )
{
WorldInfo . Game . AddInactivePRI ( PlayerReplicationInfo , self ) ;
PlayerReplicationInfo = None ;
}
reliable client event ReceiveLocalizedMessage ( class < LocalMessage > Message , optional int Switch , optional PlayerReplicationInfo RelatedPRI _1 , optional PlayerReplicationInfo RelatedPRI _2 , optional Object OptionalObject )
{
// Wait for player to be up to date with replication when joining a server, before stacking up messages
if ( WorldInfo . NetMode == NM _DedicatedServer || WorldInfo . GRI == None )
return ;
Message . Static . ClientReceive ( Self , Switch , RelatedPRI _1 , RelatedPRI _2 , OptionalObject ) ;
}
//Play a sound client side (so only client will hear it)
unreliable client event ClientPlaySound ( SoundCue ASound )
{
ClientHearSound ( ASound , self , Location , false , false ) ;
}
/** hooked up to the OnAudioFinished delegate of AudioComponents created through PlaySound() to return them to the pool */
simulated function HearSoundFinished ( AudioComponent AC )
{
HearSoundActiveComponents . RemoveItem ( AC ) ;
// if the component is already pending kill (playing actor was destroyed), then we can't put it back in the pool
if ( ! AC . IsPendingKill ( ) )
{
AC . ResetToDefaults ( ) ;
HearSoundPoolComponents [ HearSoundPoolComponents . length ] = AC ;
}
}
/ * * g e t a n a u d i o c o m p o n e n t f r o m t h e H e a r S o u n d p o o l
* creates a new component if the pool is empty and MaxConcurrentHearSounds has not been exceeded
* the component is initialized with the values passed in , ready to call Play ( ) on
* its OnAudioFinished delegate is set to this PC ' s HearSoundFinished ( ) function
* @ param ASound - the sound to play
* @ param SourceActor - the Actor to attach the sound to ( if None , attached to self )
* @ param bStopWhenOwnerDestroyed - whether the sound stops if SourceActor is destroyed
* @ param bUseLocation ( optional ) - whether to use the SourceLocation parameter for the sound 's location (otherwise, SourceActor' s location )
* @ param SourceLocation ( optional ) - if bUseLocation , the location for the sound
* @ return the AudioComponent that was found / created
* /
native function AudioComponent GetPooledAudioComponent ( SoundCue ASound , Actor SourceActor , bool bStopWhenOwnerDestroyed , optional bool bUseLocation , optional vector SourceLocation ) ;
/ * C l i e n t H e a r S o u n d ( )
Replicated function from server for replicating audible sounds played on server
* /
unreliable client event ClientHearSound ( SoundCue ASound , Actor SourceActor , vector SourceLocation , bool bStopWhenOwnerDestroyed , optional bool bIsOccluded )
{
local AudioComponent AC ;
// `log("### ClientHearSound:"@ASound@SourceActor@SourceLocation@bStopWhenOwnerDestroyed@VSize(SourceLocation - Pawn.Location));
if ( SourceActor == None )
{
AC = GetPooledAudioComponent ( ASound , SourceActor , bStopWhenOwnerDestroyed , true , SourceLocation ) ;
if ( AC == None )
{
return ;
}
AC . bUseOwnerLocation = false ;
AC . Location = SourceLocation ;
}
else if ( ( SourceActor == GetViewTarget ( ) ) || ( SourceActor == self ) )
{
AC = GetPooledAudioComponent ( ASound , None , bStopWhenOwnerDestroyed ) ;
if ( AC == None )
{
return ;
}
AC . bAllowSpatialization = false ;
}
else
{
AC = GetPooledAudioComponent ( ASound , SourceActor , bStopWhenOwnerDestroyed ) ;
if ( AC == None )
{
return ;
}
if ( ! IsZero ( SourceLocation ) && SourceLocation != SourceActor . Location )
{
AC . bUseOwnerLocation = false ;
AC . Location = SourceLocation ;
}
}
if ( bIsOccluded )
{
// if occluded reduce volume: @FIXME do something better
AC . VolumeMultiplier *= 0.5 ;
}
AC . Play ( ) ;
}
` if( ` _ _TW _WWISE _ )
/ * C l i e n t H e a r S o u n d A d v a n c e d ( )
* Replicated function from server for replicating audible sounds played on
* server that have advanced functionality and require replicating rotation
* Advanced functionality implemented in subclasses .
* /
unreliable client event ClientHearSoundAdvanced ( AkEvent ASound , Actor SourceActor , vector SourceLocation , byte CompressedSourcePitch , byte CompressedSourceYaw , byte RapidFireEnabled , bool bStopWhenOwnerDestroyed , optional bool bIsOccluded )
{
WwiseClientHearSound ( ASound , SourceActor , SourceLocation , bStopWhenOwnerDestroyed , bIsOccluded ) ;
}
/ * C l i e n t H e a r S o u n d A d v a n c e d ( )
* Replicated function from server for replicating audible sounds played on
* server that have advanced functionality and will get the rotation from a
* relevant actor . Advanced functionality implemented in subclasses .
* /
unreliable client event ClientHearSoundAdvancedRelevant ( AkEvent ASound , Actor SourceActor , vector SourceLocation , byte RapidFireEnabled , bool bStopWhenOwnerDestroyed , optional bool bIsOccluded )
{
WwiseClientHearSound ( ASound , SourceActor , SourceLocation , bStopWhenOwnerDestroyed , bIsOccluded ) ;
}
` endif //__TW_WWISE_
// WWISEMODIF_START
native unreliable client event WwiseClientHearSound ( AkEvent ASound , Actor SourceActor , vector SourceLocation , bool bStopWhenOwnerDestroyed , optional bool bIsOccluded ) ;
// WWISEMODIF_END
simulated function bool IsClosestLocalPlayerToActor ( Actor TheActor )
{
local PlayerController PC ;
local float MyDist ;
if ( ViewTarget == none )
{
return false ;
}
MyDist = VSize ( ViewTarget . Location - TheActor . Location ) ;
ForEach LocalPlayerControllers ( class 'PlayerController' , PC )
{
if ( PC != self && ( PC . ViewTarget != None ) && ( VSize ( PC . ViewTarget . Location - TheActor . Location ) < MyDist ) )
{
return false ;
}
}
return true ;
}
reliable client event Kismet _ClientPlaySound ( SoundCue ASound , Actor SourceActor , float VolumeMultiplier , float PitchMultiplier , float FadeInTime , bool bSuppressSubtitles , bool bSuppressSpatialization )
{
local AudioComponent AC ;
if ( SourceActor != None && IsClosestLocalPlayerToActor ( SourceActor ) )
{
// If we have a FaceFX animation hooked up, play that instead
// WWISEMODIF_START, alessard, nov-28-2008, WwiseAudioIntegration
if ( ASound . FaceFXAnimName != "" &&
SourceActor . PlayActorFaceFXAnim ( ASound . FaceFXAnimSetRef , ASound . FaceFXGroupName , ASound . FaceFXAnimName , ASound , none ) )
{
// Success - In case of failure, fall back to playing the sound with no Face FX animation, but there will be a warning in the log instead.
}
// WWISEMODIF_END
else
{
AC = SourceActor . CreateAudioComponent ( ASound , false , true ) ;
if ( AC != None )
{
AC . VolumeMultiplier = VolumeMultiplier ;
AC . PitchMultiplier = PitchMultiplier ;
AC . bAutoDestroy = true ;
AC . SubtitlePriority = 10000 ;
AC . bSuppressSubtitles = bSuppressSubtitles ;
AC . FadeIn ( FadeInTime , 1. f ) ;
if ( bSuppressSpatialization )
{
AC . bAllowSpatialization = false ;
}
}
}
}
}
reliable client event Kismet _ClientStopSound ( SoundCue ASound , Actor SourceActor , float FadeOutTime )
{
local AudioComponent AC , CheckAC ;
if ( SourceActor == None )
{
SourceActor = WorldInfo ;
}
foreach SourceActor . AllOwnedComponents ( class 'AudioComponent' , CheckAC )
{
if ( CheckAC . SoundCue == ASound )
{
AC = CheckAC ;
break ;
}
}
if ( AC != None )
{
AC . FadeOut ( FadeOutTime , 0. f ) ;
}
}
/** plays a FaceFX anim on the specified actor for the client */
// WWISEMODIF_START, alessard, nov-28-2008, WwiseAudioIntegration
reliable client function ClientPlayActorFaceFXAnim ( Actor SourceActor , FaceFXAnimSet AnimSet , string GroupName , string SeqName , SoundCue SoundCueToPlay , AkEvent AkEventToPlay )
{
if ( SourceActor != None )
{
SourceActor . PlayActorFaceFXAnim ( AnimSet , GroupName , SeqName , SoundCueToPlay , AkEventToPlay ) ;
}
}
// WWISEMODIF_END
reliable client event ClientMessage ( coerce string S , optional Name Type , optional float MsgLifeTime )
{
if ( WorldInfo . NetMode == NM _DedicatedServer || WorldInfo . GRI == None )
{
return ;
}
if ( Type == '' )
{
Type = 'Event' ;
}
TeamMessage ( PlayerReplicationInfo , S , Type , MsgLifeTime ) ;
}
/** Overridden by specific games */
simulated private function bool CanCommunicate ( )
{
return TRUE ;
}
simulated private function bool AllowTTSMessageFrom ( PlayerReplicationInfo PRI )
{
return TRUE ;
}
/ * *
* Text to speech handling
* /
/** Constructs a SoundCue, performs text-to-wavedata conversion. */
simulated private native function SoundCue CreateTTSSoundCue ( string StrToSpeak , PlayerReplicationInfo PRI ) ;
exec function Talk ( )
{
local Console PlayerConsole ;
local LocalPlayer LP ;
LP = LocalPlayer ( Player ) ;
if ( ( LP != None ) && CanCommunicate ( ) && ( LP . ViewportClient . ViewportConsole != None ) )
{
PlayerConsole = LocalPlayer ( Player ) . ViewportClient . ViewportConsole ;
PlayerConsole . StartTyping ( "Say " ) ;
}
}
exec function TeamTalk ( )
{
local Console PlayerConsole ;
local LocalPlayer LP ;
LP = LocalPlayer ( Player ) ;
if ( ( LP != None ) && CanCommunicate ( ) && ( LP . ViewportClient . ViewportConsole != None ) )
{
PlayerConsole = LocalPlayer ( Player ) . ViewportClient . ViewportConsole ;
PlayerConsole . StartTyping ( "TeamSay " ) ;
}
}
simulated function SpeakTTS ( coerce string S , optional PlayerReplicationInfo PRI )
{
local SoundCue Cue ;
local AudioComponent AC ;
Cue = CreateTTSSoundCue ( S , PRI ) ;
if ( Cue != None )
{
AC = CreateAudioComponent ( Cue , FALSE , TRUE , , , TRUE ) ;
AC . bAllowSpatialization = FALSE ;
AC . bAutoDestroy = TRUE ;
AC . Play ( ) ;
}
}
reliable client event TeamMessage ( PlayerReplicationInfo PRI , coerce string S , name Type , optional float MsgLifeTime )
{
local bool bIsUserCreated ;
if ( CanCommunicate ( ) )
{
if ( ( ( Type == 'Say' ) || ( Type == 'TeamSay' ) ) && ( PRI != None ) && AllowTTSMessageFrom ( PRI ) )
{
if ( ! bIsUserCreated || ( bIsUserCreated && CanViewUserCreatedContent ( ) ) )
{
SpeakTTS ( S , PRI ) ;
}
}
if ( myHUD != None )
{
myHUD . Message ( PRI , S , Type , MsgLifeTime ) ;
}
if ( ( ( Type == 'Say' ) || ( Type == 'TeamSay' ) ) && ( PRI != None ) )
{
S = PRI . PlayerName$ ": " $S ;
// This came from a user so flag as user created
bIsUserCreated = true ;
}
// since this is on the client, we can assume that if Player exists, it is a LocalPlayer
if ( Player != None )
{
// Don't allow this if the parental controls block it
if ( ! bIsUserCreated || ( bIsUserCreated && CanViewUserCreatedContent ( ) ) )
{
LocalPlayer ( Player ) . ViewportClient . ViewportConsole . OutputText ( S ) ;
}
}
}
}
function PlayBeepSound ( ) ;
/ * *
* Registers any handlers for delegates in the OnlineSubsystem . Called when a player is being created and / or ControllerId is changing .
* /
function RegisterOnlineDelegates ( )
{
local LocalPlayer LP ;
LP = LocalPlayer ( Player ) ;
// If there is an online subsystem, add our callback for UI changes
if ( OnlineSub != None && LP != None )
{
VoiceInterface = OnlineSub . VoiceInterface ;
if ( OnlineSub . SystemInterface != None )
{
// Register the callback for when external UI is shown/hidden
// This will pause/unpause a single player game based upon the UI state
OnlineSub . SystemInterface . AddExternalUIChangeDelegate ( OnExternalUIChanged ) ;
// This will pause/unpause a single player game based upon the controller state
OnlineSub . SystemInterface . AddControllerChangeDelegate ( OnControllerChanged ) ;
// This will kick you back to the IIS when you lose connection or log out of PSN
OnlineSub . SystemInterface . AddConnectionStatusChangeDelegate ( OnConnectionStatusChange ) ;
}
// Register for accepting game invites if possible
if ( OnlineSub . GameInterface != None )
{
OnlineSub . GameInterface . AddGameInviteAcceptedDelegate ( LP . ControllerId , OnGameInviteAccepted ) ;
OnlineSub . GameInterface . AddPlayTogetherStartedDelegate ( LP . ControllerId , OnPlayTogetherStarted ) ;
}
if ( OnlineSub . PartyChatInterface != None )
{
OnlineSub . PartyChatInterface . AddPartyMemberListChangedDelegate ( LP . ControllerId , OnPartyMemberListChanged ) ;
OnlineSub . PartyChatInterface . AddPartyMembersInfoChangedDelegate ( LP . ControllerId , OnPartyMembersInfoChanged ) ;
}
//@HSL_BEGIN_XBOX
if ( OnlineSub . PlayerInterface != None )
{
OnlineSub . PlayerInterface . AddPrivilegeLevelCheckedDelegate ( OnPrivilegeLevelChecked ) ;
}
//@HSL_END_XBOX
}
}
/ * *
* Is called when someone joins / leaves the player ' s party chat session
*
* @ param bJoinedOrLeft true if the player joined , false if they left
* @ param PlayerName the name of the player that was affected
* @ param PlayerId the net id of the player that left
* /
function OnPartyMemberListChanged ( bool bJoinedOrLeft , string PlayerName , UniqueNetId PlayerId ) ;
/ * *
* Is called when someone in your party chat has their custom data change ( game controlled )
*
* @ param PlayerName the name of the player that was affected
* @ param PlayerId the net id of the player that had data change
* @ param CustomData1 the first 4 bytes of the custom data
* @ param CustomData2 the second 4 bytes of the custom data
* @ param CustomData3 the third 4 bytes of the custom data
* @ param CustomData4 the fourth 4 bytes of the custom data
* /
function OnPartyMembersInfoChanged ( string PlayerName , UniqueNetId PlayerId , int CustomData1 , int CustomData2 , int CustomData3 , int CustomData4 ) ;
/ * *
* Unregisters all delegates previously registered with the online subsystem . Called when the player controller is being
* destroyed and / or replaced .
*
* @ note : in certain cases ( i . e . when the channel is closed from the server ' s end ) , the player controller will no longer have
* a reference its ULocalPlayer object . these delegates won ' t be cleared , but GC should clear the references for us .
* /
event ClearOnlineDelegates ( )
{
local LocalPlayer LP ;
` log("Clearing online delegates for" @ Self @ "(" $ ` showobj ( Player ) $ ")" , , 'DevOnline' ) ;
LP = LocalPlayer ( Player ) ;
if ( Role < ROLE _Authority || LP != None )
{
// If there is an online subsystem, clear our callback for UI/controller changes
if ( OnlineSub != None )
{
if ( OnlineSub . SystemInterface != None )
{
OnlineSub . SystemInterface . ClearExternalUIChangeDelegate ( OnExternalUIChanged ) ;
OnlineSub . SystemInterface . ClearControllerChangeDelegate ( OnControllerChanged ) ;
OnlineSub . SystemInterface . ClearConnectionStatusChangeDelegate ( OnConnectionStatusChange ) ;
}
if ( LP != None )
{
// Cleanup game invite delegate
if ( OnlineSub . GameInterface != None )
{
OnlineSub . GameInterface . ClearGameInviteAcceptedDelegate ( LP . ControllerId , OnGameInviteAccepted ) ;
OnlineSub . GameInterface . ClearPlayTogetherStartedDelegate ( LP . ControllerId , OnPlayTogetherStarted ) ;
}
if ( OnlineSub . PartyChatInterface != None )
{
OnlineSub . PartyChatInterface . ClearPartyMemberListChangedDelegate ( LP . ControllerId , OnPartyMemberListChanged ) ;
OnlineSub . PartyChatInterface . ClearPartyMembersInfoChangedDelegate ( LP . ControllerId , OnPartyMembersInfoChanged ) ;
}
//@HSL_BEGIN_XBOX
if ( OnlineSub . PlayerInterface != None )
{
OnlineSub . PlayerInterface . ClearPrivilegeLevelCheckedDelegate ( OnPrivilegeLevelChecked ) ;
}
//@HSL_END_XBOX
}
}
}
}
/** When the Controller is destroyed, the controlled pawn will need to be killed, but not the vehicle */
function CleanupPawn ( )
{
local Vehicle DrivenVehicle ;
local Pawn Driver ;
// If its a vehicle, just destroy the driver, otherwise do the normal.
DrivenVehicle = Vehicle ( Pawn ) ;
if ( DrivenVehicle != None )
{
Driver = DrivenVehicle . Driver ;
DrivenVehicle . DriverLeave ( TRUE ) ; // Force the driver out of the car
if ( Driver != None )
{
Driver . Health = 0 ;
Driver . Died ( Self , class 'DmgType_Suicided' , Driver . Location ) ;
}
}
else if ( Pawn != None )
{
Pawn . Health = 0 ;
Pawn . Died ( Self , class 'DmgType_Suicided' , Pawn . Location ) ;
}
}
event Exit ( ) { }
event Destroyed ( )
{
local int EffectIdx ;
local LocalPlayer LPlayer ;
local MaterialEffect Effect ;
local MaterialInstanceConstant MIC ;
// Disable any currently playing rumble
ClientPlayForceFeedbackWaveform ( None , None ) ;
// if this is a local player, clear all online delegates
if ( Role < ROLE _Authority || LocalPlayer ( Player ) != None )
{
ClearOnlineDelegates ( ) ;
}
// cheatmanager and playerinput cleaned up in C++ PostScriptDestroyed()
if ( Pawn != None )
{
CleanupPawn ( ) ;
}
if ( myHUD != None )
{
myHud . Destroy ( ) ;
myHUD = None ;
}
if ( PlayerCamera != None )
{
PlayerCamera . Destroy ( ) ;
PlayerCamera = None ;
}
ForceClearUnpauseDelegates ( ) ;
// remove this player's data store from the registered data stores..
UnregisterPlayerDataStores ( ) ;
// remove instanced MICs from the PP chain
LPlayer = LocalPlayer ( Player ) ;
if ( ( LPlayer != None ) && ( LPlayer . PlayerPostProcess != None ) )
{
for ( EffectIdx = 0 ; EffectIdx < LPlayer . PlayerPostProcess . Effects . Length ; EffectIdx ++ )
{
Effect = MaterialEffect ( LPlayer . PlayerPostProcess . Effects [ EffectIdx ] ) ;
if ( ( Effect != None ) && ( Effect . Material != None ) )
{
MIC = MaterialInstanceConstant ( Effect . Material ) ;
if ( ( MIC != None ) && ( MIC . Parent != None ) )
{
Effect . Material = MIC . Parent ;
}
}
}
}
Super . Destroyed ( ) ;
}
function FixFOV ( )
{
FOVAngle = Default . DefaultFOV ;
DesiredFOV = Default . DefaultFOV ;
DefaultFOV = Default . DefaultFOV ;
}
function SetFOV ( float NewFOV )
{
DesiredFOV = NewFOV ;
FOVAngle = NewFOV ;
}
function ResetFOV ( )
{
DesiredFOV = DefaultFOV ;
FOVAngle = DefaultFOV ;
}
exec function FOV ( float F )
{
if ( PlayerCamera != None )
{
PlayerCamera . SetFOV ( F ) ;
return ;
}
if ( ( F >= 80.0 ) || ( WorldInfo . NetMode == NM _Standalone ) || PlayerReplicationInfo . bOnlySpectator )
{
DefaultFOV = FClamp ( F , 80 , 100 ) ;
DesiredFOV = DefaultFOV ;
}
}
exec function Mutate ( string MutateString )
{
ServerMutate ( MutateString ) ;
}
reliable server function ServerMutate ( string MutateString )
{
if ( WorldInfo . NetMode == NM _Client )
return ;
WorldInfo . Game . Mutate ( MutateString , Self ) ;
}
// ------------------------------------------------------------------------
// Messaging functions
function bool AllowTextMessage ( string Msg )
{
local int i ;
if ( ( WorldInfo . NetMode == NM _Standalone ) || PlayerReplicationInfo . bAdmin )
return true ;
` if( ` _ _TW _ )
if ( ( WorldInfo . Pauser == none ) && ( WorldInfo . TimeSeconds - LastBroadcastTime < 1 ) )
return false ;
` else
if ( ( WorldInfo . Pauser == none ) && ( WorldInfo . TimeSeconds - LastBroadcastTime < 2 ) )
return false ;
` endif
// lower frequency if same text
if ( WorldInfo . TimeSeconds - LastBroadcastTime < 5 )
{
Msg = Left ( Msg , Clamp ( len ( Msg ) - 4 , 8 , 64 ) ) ;
for ( i = 0 ; i < 4 ; i ++ )
if ( LastBroadcastString [ i ] ~ = Msg )
return false ;
}
for ( i = 3 ; i > 0 ; i -- )
LastBroadcastString [ i ] = LastBroadcastString [ i - 1 ] ;
LastBroadcastTime = WorldInfo . TimeSeconds ;
return true ;
}
// Send a message to all players.
exec function Say ( string Msg )
{
Msg = Left ( Msg , 128 ) ;
if ( AllowTextMessage ( Msg ) )
ServerSay ( Msg ) ;
}
unreliable server function ServerSay ( string Msg )
{
local PlayerController PC ;
// center print admin messages which start with #
if ( PlayerReplicationInfo . bAdmin && left ( Msg , 1 ) == "#" )
{
Msg = right ( Msg , len ( Msg ) - 1 ) ;
foreach WorldInfo . AllControllers ( class 'PlayerController' , PC )
{
PC . ClientAdminMessage ( Msg ) ;
}
return ;
}
WorldInfo . Game . Broadcast ( self , Msg , 'Say' ) ;
}
reliable client function ClientAdminMessage ( string Msg )
{
local LocalPlayer LP ;
LP = LocalPlayer ( Player ) ;
if ( LP != None )
{
LP . ViewportClient . ClearProgressMessages ( ) ;
LP . ViewportClient . SetProgressTime ( 6 ) ;
LP . ViewportClient . SetProgressMessage ( PMT _AdminMessage , Msg ) ;
}
}
exec function TeamSay ( string Msg )
{
Msg = Left ( Msg , 128 ) ;
if ( AllowTextMessage ( Msg ) )
ServerTeamSay ( Msg ) ;
}
unreliable server function ServerTeamSay ( string Msg )
{
LastActiveTime = WorldInfo . TimeSeconds ;
if ( ! WorldInfo . GRI . GameClass . Default . bTeamGame )
{
Say ( Msg ) ;
return ;
}
WorldInfo . Game . BroadcastTeam ( self , Msg , 'TeamSay' ) ;
}
// ------------------------------------------------------------------------
/ * *
* Called when the local player is about to travel to a new map or IP address . Provides subclass with an opportunity
* to perform cleanup or other tasks prior to the travel .
* /
event PreClientTravel ( string PendingURL , ETravelType TravelType , bool bIsSeamlessTravel )
{
local UIInteraction UIController ;
local GameUISceneClient GameSceneClient ;
// notify the UI system that we're about to perform a travel.
UIController = GetUIController ( ) ;
if ( UIController != None && IsPrimaryPlayer ( ) )
{
GameSceneClient = UIController . SceneClient ;
if ( GameSceneClient != None )
{
GameSceneClient . NotifyClientTravel ( Self , PendingURL , TravelType , bIsSeamlessTravel ) ;
}
if ( PlayerInput != none )
{
PlayerInput . PreClientTravel ( PendingURL , TravelType , bIsSeamlessTravel ) ;
}
}
}
/ * *
* Change Camera mode
*
* @ param New camera mode to set
* /
exec function Camera ( name NewMode )
{
ServerCamera ( NewMode ) ;
}
reliable server function ServerCamera ( name NewMode )
{
if ( NewMode == '1st' )
{
NewMode = 'FirstPerson' ;
}
else if ( NewMode == '3rd' )
{
NewMode = 'ThirdPerson' ;
}
SetCameraMode ( NewMode ) ;
` if( ` notdefined ( FINAL _RELEASE ) )
if ( PlayerCamera != None )
` log("#### " $ PlayerCamera.CameraStyle);
` endif
}
/ * *
* Replicated function to set camera style on client
*
* @ param NewCamMode , name defining the new camera mode
* /
reliable client function ClientSetCameraMode ( name NewCamMode )
{
if ( PlayerCamera != None )
PlayerCamera . CameraStyle = NewCamMode ;
}
/ * *
* Set new camera mode
*
* @ param NewCamMode , new camera mode .
* /
function SetCameraMode ( name NewCamMode )
{
if ( PlayerCamera != None )
{
PlayerCamera . CameraStyle = NewCamMode ;
if ( WorldInfo . NetMode == NM _DedicatedServer )
{
ClientSetCameraMode ( NewCamMode ) ;
}
}
}
/ * *
* Reset Camera Mode to default
* /
event ResetCameraMode ( )
{
if ( Pawn != None )
{ // If we have a pawn, let's ask it which camera mode we should use
SetCameraMode ( Pawn . GetDefaultCameraMode ( Self ) ) ;
}
else
{ // otherwise default to first person view.
SetCameraMode ( 'FirstPerson' ) ;
}
}
reliable client event ClientSetCameraFade ( bool bEnableFading , optional color FadeColor , optional vector2d FadeAlpha , optional float FadeTime , optional bool bFadeAudio )
{
if ( PlayerCamera != None )
{
PlayerCamera . bEnableFading = bEnableFading ;
if ( PlayerCamera . bEnableFading )
{
PlayerCamera . FadeColor = FadeColor ;
PlayerCamera . FadeAlpha = FadeAlpha ;
PlayerCamera . FadeTime = FadeTime ;
PlayerCamera . FadeTimeRemaining = FadeTime ;
PlayerCamera . bFadeAudio = bFadeAudio ;
}
else
{
// Make sure FadeAmount finishes at the correct value
PlayerCamera . FadeAmount = PlayerCamera . FadeAlpha . Y ;
// Remove any previous fade color.
PlayerCamera . FadeColor = FadeColor ;
if ( PlayerCamera . bFadeAudio )
{
PlayerCamera . ApplyAudioFade ( ) ;
}
}
}
}
/ * *
* return whether viewing in first person mode
* /
function bool UsingFirstPersonCamera ( )
{
return ( ( PlayerCamera == None ) || ( PlayerCamera . CameraStyle == 'FirstPerson' ) ) && LocalPlayer ( Player ) != None ;
}
/ * F o r c e D e a t h U p d a t e ( )
Make sure ClientAdjustPosition immediately informs client of pawn ' s death
* /
function ForceDeathUpdate ( )
{
LastUpdateTime = WorldInfo . TimeSeconds - 10 ;
}
/ * D u a l S e r v e r M o v e ( )
- replicated function sent by client to server - contains client movement and firing info for two moves
* /
unreliable server function DualServerMove
(
float TimeStamp0 ,
vector InAccel0 ,
byte PendingFlags ,
int View0 ,
float TimeStamp ,
vector InAccel ,
vector ClientLoc ,
byte NewFlags ,
byte ClientRoll ,
` if( ` _ _TW _ )
int View ,
optional int FreeAimRot0 ,
optional int FreeAimRot
` else
int View
` endif
)
{
ServerMove ( TimeStamp0 , InAccel0 , vect ( 1 , 2 , 3 ) , PendingFlags , ClientRoll , View0 ) ;
ServerMove ( TimeStamp , InAccel , ClientLoc , NewFlags , ClientRoll , View ) ;
}
/ * O l d S e r v e r M o v e ( )
- resending an ( important ) old move . Process it if not already processed .
* /
unreliable server function OldServerMove
(
float OldTimeStamp ,
byte OldAccelX ,
byte OldAccelY ,
byte OldAccelZ ,
byte OldMoveFlags
)
{
local vector Accel ;
if ( AcknowledgedPawn != Pawn )
return ;
if ( CurrentTimeStamp < OldTimeStamp - 0.001 )
{
// split out components of lost move (approx)
Accel . X = OldAccelX ;
if ( Accel . X > 127 )
Accel . X = - 1 * ( Accel . X - 128 ) ;
Accel . Y = OldAccelY ;
if ( Accel . Y > 127 )
Accel . Y = - 1 * ( Accel . Y - 128 ) ;
Accel . Z = OldAccelZ ;
if ( Accel . Z > 127 )
Accel . Z = - 1 * ( Accel . Z - 128 ) ;
Accel *= 20 ;
//`log("Recovered move from "$OldTimeStamp$" acceleration "$Accel$" from "$OldAccel);
OldTimeStamp = FMin ( OldTimeStamp , CurrentTimeStamp + MaxResponseTime ) ;
MoveAutonomous ( OldTimeStamp - CurrentTimeStamp , OldMoveFlags , Accel , rot ( 0 , 0 , 0 ) ) ;
CurrentTimeStamp = OldTimeStamp ;
}
}
/** @return time delta to use for the current ServerMove() */
function float GetServerMoveDeltaTime ( float TimeStamp )
{
local float DeltaTime ;
DeltaTime = FMin ( MaxResponseTime , TimeStamp - CurrentTimeStamp ) ;
if ( Pawn == None )
{
bWasSpeedHack = FALSE ;
ResetTimeMargin ( ) ;
}
else if ( ! CheckSpeedHack ( DeltaTime ) )
{
if ( ! bWasSpeedHack )
{
if ( WorldInfo . TimeSeconds - LastSpeedHackLog > 20 )
{
` log("Possible speed hack by " $ PlayerReplicationInfo.PlayerName);
LastSpeedHackLog = WorldInfo . TimeSeconds ;
}
ClientMessage ( "Speed Hack Detected!" , 'CriticalEvent' ) ;
}
else
{
bWasSpeedHack = TRUE ;
}
DeltaTime = 0 ;
Pawn . Velocity = vect ( 0 , 0 , 0 ) ;
}
else
{
DeltaTime *= Pawn . CustomTimeDilation ;
bWasSpeedHack = FALSE ;
}
return DeltaTime ;
}
/ * * c a l l e d a f t e r m o v e m e n t i n S e r v e r M o v e ( ) t o c h e c k f o r a n d h a n d l e a n y p o t e n t i a l e r r o r b e t w e e n s e r v e r a n d c l i e n t p o s i t i o n
* by setting PendingAdjustment appropriately
* /
function ServerMoveHandleClientError ( float TimeStamp , vector Accel , vector ClientLoc )
{
local float ClientErr ;
local vector LocDiff ;
// Accumulate movement error.
if ( ClientLoc == vect ( 1 , 2 , 3 ) )
{
return ; // first part of double servermove
}
else if ( WorldInfo . TimeSeconds - LastUpdateTime < CLIENTADJUSTUPDATECOST / Player . CurrentNetSpeed )
{
// limit frequency of corrections if connection speed is limited
return ;
}
if ( Pawn == None )
{
LocDiff = Location - ClientLoc ;
}
else if ( Pawn . bForceRMVelocity && Pawn . default . Mesh . RootMotionMode == RMM _Ignore )
{
// don't do corrections during root motion
LocDiff = vect ( 0 , 0 , 0 ) ;
}
// force a correction periodically if the Pawn is not moving
// this handles cases where the client incorrectly thinks movement is impossible
// since without movement on one side or the other error can never be accumulated
else if ( Pawn . Physics != PHYS _None && WorldInfo . TimeSeconds - LastUpdateTime > 1.0 && IsZero ( Accel ) )
{
LocDiff = vect ( 1000 , 1000 , 1000 ) ;
}
else
{
LocDiff = Pawn . Location - ClientLoc ;
}
ClientErr = LocDiff Dot LocDiff ;
// If client has accumulated a noticeable positional error, correct him.
if ( ClientErr > MAXPOSITIONERRORSQUARED )
{
if ( Pawn == None )
{
PendingAdjustment . newPhysics = Physics ;
PendingAdjustment . NewLoc = Location ;
PendingAdjustment . NewVel = Velocity ;
}
else
{
PendingAdjustment . newPhysics = Pawn . Physics ;
PendingAdjustment . NewVel = Pawn . Velocity ;
PendingAdjustment . NewBase = Pawn . Base ;
if ( ( InterpActor ( Pawn . Base ) != None ) || ( Vehicle ( Pawn . Base ) != None ) || DynamicBlockingVolume ( Pawn . Base ) != None )
{
PendingAdjustment . NewLoc = Pawn . Location - Pawn . Base . Location ;
}
else
{
PendingAdjustment . NewLoc = Pawn . Location ;
}
PendingAdjustment . NewFloor = Pawn . Floor ;
}
//`log("Client Error at" @ TimeStamp @ "is" @ ClientErr @ "with acceleration" @ Accel @ "LocDiff" @ LocDiff @ "Physics" @ Pawn.Physics);
LastUpdateTime = WorldInfo . TimeSeconds ;
PendingAdjustment . TimeStamp = TimeStamp ;
PendingAdjustment . bAckGoodMove = 0 ;
}
else
{
// acknowledge receipt of this successful servermove()
PendingAdjustment . TimeStamp = TimeStamp ;
PendingAdjustment . bAckGoodMove = 1 ;
}
}
/ * S e r v e r M o v e ( )
- replicated function sent by client to server - contains client movement and firing info .
* /
unreliable server function ServerMove
(
float TimeStamp ,
vector InAccel ,
vector ClientLoc ,
byte MoveFlags ,
byte ClientRoll ,
` if( ` _ _TW _ )
int View ,
optional int FreeAimRot
` else
int View
` endif
)
{
local float DeltaTime ;
local rotator DeltaRot , Rot , ViewRot ;
local vector Accel ;
local int maxPitch , ViewPitch , ViewYaw ;
// If this move is outdated, discard it.
if ( CurrentTimeStamp >= TimeStamp )
{
return ;
}
if ( AcknowledgedPawn != Pawn )
{
InAccel = vect ( 0 , 0 , 0 ) ;
GivePawn ( Pawn ) ;
}
// View components
ViewPitch = ( View & 65535 ) ;
ViewYaw = ( View >> 16 ) ;
// Acceleration was scaled by 10x for replication, to keep more precision since vectors are rounded for replication
Accel = InAccel * 0.1 ;
// Save move parameters.
DeltaTime = GetServerMoveDeltaTime ( TimeStamp ) ;
CurrentTimeStamp = TimeStamp ;
ServerTimeStamp = WorldInfo . TimeSeconds ;
ViewRot . Pitch = ViewPitch ;
ViewRot . Yaw = ViewYaw ;
ViewRot . Roll = 0 ;
if ( InAccel != vect ( 0 , 0 , 0 ) )
{
LastActiveTime = WorldInfo . TimeSeconds ;
}
SetRotation ( ViewRot ) ;
if ( AcknowledgedPawn != Pawn )
{
return ;
}
if ( Pawn != None )
{
Rot . Roll = 256 * ClientRoll ;
Rot . Yaw = ViewYaw ;
if ( ( Pawn . Physics == PHYS _Swimming ) || ( Pawn . Physics == PHYS _Flying ) )
{
maxPitch = 2 ;
}
else
{
maxPitch = 0 ;
}
if ( ( ViewPitch > maxPitch * Pawn . MaxPitchLimit ) && ( ViewPitch < 65536 - maxPitch * Pawn . MaxPitchLimit ) )
{
if ( ViewPitch < 32768 )
{
Rot . Pitch = maxPitch * Pawn . MaxPitchLimit ;
}
else
{
Rot . Pitch = 65536 - maxPitch * Pawn . MaxPitchLimit ;
}
}
else
{
Rot . Pitch = ViewPitch ;
}
DeltaRot = ( Rotation - Rot ) ;
Pawn . FaceRotation ( Rot , DeltaTime ) ;
}
// Perform actual movement
if ( ( WorldInfo . Pauser == None ) && ( DeltaTime > 0 ) )
{
MoveAutonomous ( DeltaTime , MoveFlags , Accel , DeltaRot ) ;
}
ServerMoveHandleClientError ( TimeStamp , Accel , ClientLoc ) ;
//`log("Server moved stamp "$TimeStamp$" location "$Pawn.Location$" Acceleration "$Pawn.Acceleration$" Velocity "$Pawn.Velocity);
}
/ * C a l l e d o n s e r v e r a t e n d o f t i c k w h e n P e n d i n g A d j u s t m e n t h a s b e e n s e t .
Done this way to avoid ever sending more than one ClientAdjustment per server tick .
* /
event SendClientAdjustment ( )
{
if ( AcknowledgedPawn != Pawn )
{
PendingAdjustment . TimeStamp = 0 ;
return ;
}
if ( PendingAdjustment . bAckGoodMove == 1 )
{
// just notify client this move was received
ClientAckGoodMove ( PendingAdjustment . TimeStamp ) ;
}
else if ( ( Pawn == None ) || ( Pawn . Physics != PHYS _Spider ) )
{
if ( PendingAdjustment . NewVel == vect ( 0 , 0 , 0 ) )
{
if ( GetStateName ( ) == 'PlayerWalking' && Pawn != None && Pawn . Physics == PHYS _Walking )
{
VeryShortClientAdjustPosition
(
PendingAdjustment . TimeStamp ,
PendingAdjustment . NewLoc . X ,
PendingAdjustment . NewLoc . Y ,
PendingAdjustment . NewLoc . Z ,
PendingAdjustment . NewBase
) ;
}
else
{
ShortClientAdjustPosition
(
PendingAdjustment . TimeStamp ,
GetStateName ( ) ,
PendingAdjustment . newPhysics ,
PendingAdjustment . NewLoc . X ,
PendingAdjustment . NewLoc . Y ,
PendingAdjustment . NewLoc . Z ,
PendingAdjustment . NewBase
) ;
}
}
else
{
ClientAdjustPosition
(
PendingAdjustment . TimeStamp ,
GetStateName ( ) ,
PendingAdjustment . newPhysics ,
PendingAdjustment . NewLoc . X ,
PendingAdjustment . NewLoc . Y ,
PendingAdjustment . NewLoc . Z ,
PendingAdjustment . NewVel . X ,
PendingAdjustment . NewVel . Y ,
PendingAdjustment . NewVel . Z ,
PendingAdjustment . NewBase
) ;
}
}
else
{
LongClientAdjustPosition
(
PendingAdjustment . TimeStamp ,
GetStateName ( ) ,
PendingAdjustment . newPhysics ,
PendingAdjustment . NewLoc . X ,
PendingAdjustment . NewLoc . Y ,
PendingAdjustment . NewLoc . Z ,
PendingAdjustment . NewVel . X ,
PendingAdjustment . NewVel . Y ,
PendingAdjustment . NewVel . Z ,
PendingAdjustment . NewBase ,
PendingAdjustment . NewFloor . X ,
PendingAdjustment . NewFloor . Y ,
PendingAdjustment . NewFloor . Z
) ;
}
PendingAdjustment . TimeStamp = 0 ;
PendingAdjustment . bAckGoodMove = 0 ;
}
// Only executed on server
unreliable server function ServerDrive ( float InForward , float InStrafe , float aUp , bool InJump , int View )
{
local rotator ViewRotation ;
ViewRotation . Pitch = ( View & 65535 ) ;
ViewRotation . Yaw = ( View >> 16 ) ;
ViewRotation . Roll = 0 ;
SetRotation ( ViewRotation ) ;
ProcessDrive ( InForward , InStrafe , aUp , InJump ) ;
}
function ProcessDrive ( float InForward , float InStrafe , float InUp , bool InJump )
{
ClientGotoState ( GetStateName ( ) , 'Begin' ) ;
}
function ProcessMove ( float DeltaTime , vector newAccel , eDoubleClickDir DoubleClickMove , rotator DeltaRot )
{
if ( ( Pawn != None ) && ( Pawn . Acceleration != newAccel ) )
{
Pawn . Acceleration = newAccel ;
}
}
function MoveAutonomous
(
float DeltaTime ,
byte CompressedFlags ,
vector newAccel ,
rotator DeltaRot
)
{
local EDoubleClickDir DoubleClickMove ;
if ( ( Pawn != None ) && Pawn . bHardAttach )
return ;
DoubleClickMove = SavedMoveClass . static . SetFlags ( CompressedFlags , self ) ;
HandleWalking ( ) ;
ProcessMove ( DeltaTime , newAccel , DoubleClickMove , DeltaRot ) ;
if ( Pawn != None )
{
Pawn . AutonomousPhysics ( DeltaTime ) ;
}
else
{
AutonomousPhysics ( DeltaTime ) ;
}
bDoubleJump = false ;
//`log("Role "$Role$" moveauto time "$100 * DeltaTime$" ("$WorldInfo.TimeDilation$")");
}
/ * V e r y S h o r t C l i e n t A d j u s t P o s i t i o n
bandwidth saving version , when velocity is zeroed , and pawn is walking
* /
unreliable client function VeryShortClientAdjustPosition
(
float TimeStamp ,
float NewLocX ,
float NewLocY ,
float NewLocZ ,
Actor NewBase
)
{
local vector Floor ;
if ( Pawn != None )
{
Floor = Pawn . Floor ;
}
LongClientAdjustPosition ( TimeStamp , 'PlayerWalking' , PHYS _Walking , NewLocX , NewLocY , NewLocZ , 0 , 0 , 0 , NewBase , Floor . X , Floor . Y , Floor . Z ) ;
}
/ * S h o r t C l i e n t A d j u s t P o s i t i o n
bandwidth saving version , when velocity is zeroed
* /
unreliable client function ShortClientAdjustPosition
(
float TimeStamp ,
name newState ,
EPhysics newPhysics ,
float NewLocX ,
float NewLocY ,
float NewLocZ ,
Actor NewBase
)
{
local vector Floor ;
if ( Pawn != None )
{
Floor = Pawn . Floor ;
}
LongClientAdjustPosition ( TimeStamp , newState , newPhysics , NewLocX , NewLocY , NewLocZ , 0 , 0 , 0 , NewBase , Floor . X , Floor . Y , Floor . Z ) ;
}
reliable client function ClientCapBandwidth ( int Cap )
{
ClientCap = Cap ;
if ( ( Player != None ) && ( Player . CurrentNetSpeed > Cap ) )
{
SetNetSpeed ( Cap ) ;
}
}
unreliable client function ClientAckGoodMove ( float TimeStamp )
{
UpdatePing ( TimeStamp ) ;
CurrentTimeStamp = TimeStamp ;
ClearAckedMoves ( ) ;
}
/ * C l i e n t A d j u s t P o s i t i o n
- pass newloc and newvel in components so they don ' t get rounded
* /
unreliable client function ClientAdjustPosition
(
float TimeStamp ,
name newState ,
EPhysics newPhysics ,
float NewLocX ,
float NewLocY ,
float NewLocZ ,
float NewVelX ,
float NewVelY ,
float NewVelZ ,
Actor NewBase
)
{
local vector Floor ;
if ( Pawn != None )
{
Floor = Pawn . Floor ;
}
LongClientAdjustPosition ( TimeStamp , newState , newPhysics , NewLocX , NewLocY , NewLocZ , NewVelX , NewVelY , NewVelZ , NewBase , Floor . X , Floor . Y , Floor . Z ) ;
}
/ * e p i c = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
* : : UpdatePing
update average ping based on newly received round trip timestamp .
Occasionally send ping updates to the server , and also adjust netspeed if connection appears to be saturated
* /
final function UpdatePing ( float TimeStamp )
{
if ( PlayerReplicationInfo != None )
{
PlayerReplicationInfo . UpdatePing ( TimeStamp ) ;
if ( WorldInfo . TimeSeconds - LastPingUpdate > 4 )
{
LastPingUpdate = WorldInfo . TimeSeconds ;
ServerUpdatePing ( 1000 * PlayerReplicationInfo . ExactPing ) ;
}
}
}
/ * * @ r e t u r n w h e t h e r w e s h o u l d s k i p t h e p o s i t i o n u p d a t i n g i n L o n g C l i e n t A d j u s t P o s i t i o n ( ) d u e t o c u r r e n t o r u p c o m i n g r o o t m o t i o n
* ( does not affect move acknowledgement or state transitions )
* /
function bool SkipPositionUpdateForRM ( )
{
local SavedMove CurrentMove ;
// avoiding correcting for temporary root motion moves; wait until the animation completes
if ( Pawn != None && Pawn . default . Mesh . RootMotionMode == RMM _Ignore )
{
if ( Pawn . Physics != PHYS _Falling && Pawn . Mesh != None &&
Pawn . Mesh . RootMotionMode != RMM _Ignore && ! Pawn . bRootMotionFromInterpCurve )
{
` log("- skipping position update for root motion",,'PlayerMove');
return true ;
}
// don't correct if have upcoming root motion
CurrentMove = SavedMoves ;
while ( CurrentMove != None )
{
if ( CurrentMove . bForceRMVelocity )
{
` log("- skipping position update for upcoming root motion",,'PlayerMove');
return true ;
}
CurrentMove = CurrentMove . NextMove ;
}
}
return false ;
}
/ * L o n g C l i e n t A d j u s t P o s i t i o n
long version , when care about pawn ' s floor normal
* /
unreliable client function LongClientAdjustPosition
(
float TimeStamp ,
name newState ,
EPhysics newPhysics ,
float NewLocX ,
float NewLocY ,
float NewLocZ ,
float NewVelX ,
float NewVelY ,
float NewVelZ ,
Actor NewBase ,
float NewFloorX ,
float NewFloorY ,
float NewFloorZ
)
{
local vector NewLocation , NewVelocity , NewFloor ;
local Actor MoveActor ;
local SavedMove CurrentMove ;
local Actor TheViewTarget ;
` if( ` notdefined ( FINAL _RELEASE ) )
local Vector OldLoc ;
OldLoc = ( Pawn != None ) ? Pawn . Location : Location ;
` endif
UpdatePing ( TimeStamp ) ;
if ( Pawn != None )
{
if ( Pawn . bTearOff )
{
Pawn = None ;
if ( ! GamePlayEndedState ( ) && ! IsInState ( 'Dead' ) )
{
GotoState ( 'Dead' ) ;
}
return ;
}
MoveActor = Pawn ;
TheViewTarget = GetViewTarget ( ) ;
if ( ( TheViewTarget != Pawn )
&& ( ( TheViewTarget == self ) || ( ( Pawn ( TheViewTarget ) != None ) && ( Pawn ( TheViewTarget ) . Health <= 0 ) ) ) )
{
ResetCameraMode ( ) ;
SetViewTarget ( Pawn ) ;
}
}
else
{
MoveActor = self ;
if ( GetStateName ( ) != newstate )
{
` log("- state change:"@GetStateName()@"->"@newstate,,'PlayerMove');
if ( NewState == 'RoundEnded' )
{
GotoState ( NewState ) ;
}
else if ( IsInState ( 'Dead' ) )
{
if ( ( NewState != 'PlayerWalking' ) && ( NewState != 'PlayerSwimming' ) )
{
GotoState ( NewState ) ;
}
return ;
}
else if ( NewState == 'Dead' )
{
GotoState ( NewState ) ;
}
}
}
if ( CurrentTimeStamp >= TimeStamp )
{
return ;
}
CurrentTimeStamp = TimeStamp ;
NewLocation . X = NewLocX ;
NewLocation . Y = NewLocY ;
NewLocation . Z = NewLocZ ;
NewVelocity . X = NewVelX ;
NewVelocity . Y = NewVelY ;
NewVelocity . Z = NewVelZ ;
// skip update if no error
CurrentMove = SavedMoves ;
// note that acked moves are cleared here, instead of calling ClearAckedMoves()
while ( CurrentMove != None )
{
if ( CurrentMove . TimeStamp <= CurrentTimeStamp )
{
SavedMoves = CurrentMove . NextMove ;
CurrentMove . NextMove = FreeMoves ;
FreeMoves = CurrentMove ;
if ( CurrentMove . TimeStamp == CurrentTimeStamp )
{
LastAckedAccel = CurrentMove . Acceleration ;
FreeMoves . Clear ( ) ;
if ( ( ( InterpActor ( NewBase ) != None ) || ( Vehicle ( NewBase ) != None ) || DynamicBlockingVolume ( NewBase ) != None )
&& ( NewBase == CurrentMove . EndBase ) )
{
if ( ( GetStateName ( ) == NewState )
&& IsInState ( 'PlayerWalking' )
&& ( ( MoveActor . Physics == PHYS _Walking ) || ( MoveActor . Physics == PHYS _Falling ) ) )
{
if ( VSizeSq ( CurrentMove . SavedRelativeLocation - NewLocation ) < MAXPOSITIONERRORSQUARED )
{
CurrentMove = None ;
return ;
}
else if ( ( Vehicle ( NewBase ) != None )
&& ( VSizeSq ( Velocity ) < MAXNEARZEROVELOCITYSQUARED ) && ( VSizeSq ( NewVelocity ) < MAXNEARZEROVELOCITYSQUARED )
&& ( VSizeSq ( CurrentMove . SavedRelativeLocation - NewLocation ) < MAXVEHICLEPOSITIONERRORSQUARED ) )
{
CurrentMove = None ;
return ;
}
}
}
else if ( ( VSizeSq ( CurrentMove . SavedLocation - NewLocation ) < MAXPOSITIONERRORSQUARED )
&& ( VSizeSq ( CurrentMove . SavedVelocity - NewVelocity ) < MAXNEARZEROVELOCITYSQUARED )
&& ( GetStateName ( ) == NewState )
&& IsInState ( 'PlayerWalking' )
&& ( ( MoveActor . Physics == PHYS _Walking ) || ( MoveActor . Physics == PHYS _Falling ) ) )
{
CurrentMove = None ;
return ;
}
CurrentMove = None ;
}
else
{
FreeMoves . Clear ( ) ;
CurrentMove = SavedMoves ;
}
}
else
{
CurrentMove = None ;
}
}
if ( MoveActor . bHardAttach )
{
if ( MoveActor . Base == None || MoveActor . Base . bWorldGeometry )
{
if ( NewBase != None )
{
MoveActor . SetLocation ( NewLocation ) ;
MoveActor . SetPhysics ( NewPhysics ) ;
MoveActor . SetBase ( NewBase ) ;
}
if ( MoveActor . Base == None )
{
MoveActor . SetHardAttach ( false ) ;
}
else
{
return ;
}
}
else
{
return ;
}
}
NewFloor . X = NewFloorX ;
NewFloor . Y = NewFloorY ;
NewFloor . Z = NewFloorZ ;
//@debug - track down the errors
` log("- base mismatch:"@MoveActor.Base@NewBase,MoveActor.Base != NewBase,'PlayerMove');
` log("- location mismatch, delta:"@VSize(MoveActor.Location - NewLocation),MoveActor.Location != NewLocation,'PlayerMove');
` log("- velocity mismatch, delta:"@VSize(NewVelocity - MoveActor.Velocity)@"client:"@VSize(MoveActor.Velocity)@"server:"@VSize(NewVelocity),MoveActor.Velocity != NewVelocity,'PlayerMove');
if ( SkipPositionUpdateForRM ( ) )
{
return ;
}
if ( ( InterpActor ( NewBase ) != None ) || ( Vehicle ( NewBase ) != None ) || DynamicBlockingVolume ( NewBase ) != None )
{
NewLocation += NewBase . Location ;
}
// `log("Client "$Role$" adjust "$self$" stamp "$TimeStamp$" location "$MoveActor.Location);
MoveActor . bCanTeleport = FALSE ;
if ( ! MoveActor . SetLocation ( NewLocation ) && ( Pawn ( MoveActor ) != None )
&& ( Pawn ( MoveActor ) . CylinderComponent . CollisionHeight > Pawn ( MoveActor ) . CrouchHeight )
&& ! Pawn ( MoveActor ) . bIsCrouched
&& ( newPhysics == PHYS _Walking )
&& ( MoveActor . Physics != PHYS _RigidBody ) )
{
MoveActor . SetPhysics ( newPhysics ) ;
if ( ! MoveActor . SetLocation ( NewLocation + vect ( 0 , 0 , 1 ) * Pawn ( MoveActor ) . MaxStepHeight ) )
{
` if( ` _ _TW _ )
// Disabled ForceCrouch. This is not replicated, not particularly useful, and a little obnoxious.
` else
Pawn ( MoveActor ) . ForceCrouch ( ) ;
MoveActor . SetLocation ( NewLocation ) ;
` endif
}
else
{
MoveActor . MoveSmooth ( vect ( 0 , 0 , - 1 ) * Pawn ( MoveActor ) . MaxStepHeight ) ;
}
}
MoveActor . bCanTeleport = TRUE ;
// Hack. Don't let network change physics mode of rigid body stuff on the client.
if ( MoveActor . Physics != PHYS _RigidBody &&
newPhysics != PHYS _RigidBody )
{
MoveActor . SetPhysics ( newPhysics ) ;
}
if ( MoveActor != self )
{
MoveActor . SetBase ( NewBase , NewFloor ) ;
}
MoveActor . Velocity = NewVelocity ;
UpdateStateFromAdjustment ( NewState ) ;
bUpdatePosition = TRUE ;
` if( ` notdefined ( FINAL _RELEASE ) )
if ( bDebugClientAdjustPosition )
{
DrawDebugBox ( OldLoc , vect ( 2 , 2 , 2 ) , 0 , 120 , 0 , TRUE ) ;
DrawDebugBox ( Pawn . Location , vect ( 3 , 3 , 3 ) , 255 , 255 , 255 , TRUE ) ;
DrawDebugLine ( Pawn . Location , OldLoc , 255 , 255 , 255 , TRUE ) ;
` log( ` location @ "!!!!!!!!!!!!!!" @ SavedMoves @ ` showvar(Pawn.Rotation)@ ` showvar ( WorldInfo . TimeSeconds ) ) ;
}
` endif
}
/ * *
Called by LongClientAdjustPosition ( )
@ param NewState is the state recommended by the server
* /
function UpdateStateFromAdjustment ( name NewState )
{
if ( GetStateName ( ) != newstate )
{
GotoState ( newstate ) ;
}
}
unreliable server function ServerUpdatePing ( int NewPing )
{
PlayerReplicationInfo . Ping = Min ( 0.25 * NewPing , 250 ) ;
}
/ * C l e a r A c k e d M o v e s ( )
clear out acknowledged / missed sent moves
* /
function ClearAckedMoves ( )
{
local SavedMove CurrentMove ;
CurrentMove = SavedMoves ;
while ( CurrentMove != None )
{
if ( CurrentMove . TimeStamp <= CurrentTimeStamp )
{
if ( CurrentMove . TimeStamp == CurrentTimeStamp )
LastAckedAccel = CurrentMove . Acceleration ;
SavedMoves = CurrentMove . NextMove ;
CurrentMove . NextMove = FreeMoves ;
FreeMoves = CurrentMove ;
FreeMoves . Clear ( ) ;
CurrentMove = SavedMoves ;
}
else
break ;
}
}
/ * *
* Called from PlayerTick after receiving ClientAdjustPosition call from server ( and setting the bUpdating flag )
* Client has already had position information corrected
*
* This function plays through previously saved moves that haven ' t been acknowledged by the server , predicting where the client
* should be after the server correction
* /
function ClientUpdatePosition ( )
{
local SavedMove CurrentMove ;
local int realbRun , realbDuck ;
local bool bRealJump ;
local bool bRealPreciseDestination ;
local bool bRealForceMaxAccel ;
local bool bRealRootMotionFromInterpCurve ;
local ERootMotionMode RealRootMotionMode ;
` if( ` notdefined ( FINAL _RELEASE ) )
local Vector OldLoc ;
` endif
bUpdatePosition = FALSE ;
// Dont do any network position updates on things running PHYS_RigidBody
if ( Pawn != None && Pawn . Physics == PHYS _RigidBody )
{
return ;
}
` if( ` notdefined ( FINAL _RELEASE ) )
if ( bDebugClientAdjustPosition )
{
` log( ` location @ "!!!!!!!!!!!!!!" @ SavedMoves @ ` showvar(Pawn.Rotation)@ ` showvar ( WorldInfo . TimeSeconds ) ) ;
}
` endif
realbRun = bRun ;
realbDuck = bDuck ;
bRealJump = bPressedJump ;
bUpdating = TRUE ;
bRealPreciseDestination = bPreciseDestination ;
if ( Pawn != None )
{
bRealForceMaxAccel = Pawn . bForceMaxAccel ;
bRealRootMotionFromInterpCurve = Pawn . bRootMotionFromInterpCurve ;
RealRootMotionMode = Pawn . Mesh . RootMotionMode ;
}
ClearAckedMoves ( ) ;
CurrentMove = SavedMoves ;
while ( CurrentMove != None )
{
if ( ( PendingMove == CurrentMove ) && ( Pawn != None ) )
{
PendingMove . SetInitialPosition ( Pawn ) ;
}
` if( ` notdefined ( FINAL _RELEASE ) )
if ( bDebugClientAdjustPosition )
{
` log( CurrentMove.GetDebugString() );
` log( "Old"@Pawn.Location@Pawn.bRootMotionFromInterpCurve@Pawn.RootMotionInterpCurrentTime );
OldLoc = Pawn . Location ;
}
` endif
CurrentMove . PrepMoveFor ( Pawn ) ;
MoveAutonomous ( CurrentMove . Delta , CurrentMove . CompressedFlags ( ) , CurrentMove . Acceleration , rot ( 0 , 0 , 0 ) ) ;
CurrentMove . ResetMoveFor ( Pawn ) ;
` if( ` notdefined ( FINAL _RELEASE ) )
if ( bDebugClientAdjustPosition )
{
` log( "New"@Pawn.Location@Pawn.bRootMotionFromInterpCurve@Pawn.RootMotionInterpCurrentTime );
DrawDebugBox ( OldLoc , vect ( 4 , 4 , 4 ) , 120 , 0 , 0 , TRUE ) ;
DrawDebugBox ( Pawn . Location , vect ( 5 , 5 , 5 ) , 0 , 0 , 120 , TRUE ) ;
DrawDebugLine ( OldLoc + vect ( 0 , 0 , 2 ) , Pawn . Location + vect ( 0 , 0 , 2 ) , 0 , 120 , 0 , TRUE ) ;
}
` endif
CurrentMove = CurrentMove . NextMove ;
}
bUpdating = FALSE ;
bDuck = realbDuck ;
bRun = realbRun ;
bPressedJump = bRealJump ;
bPreciseDestination = bRealPreciseDestination ;
if ( Pawn != None )
{
Pawn . bForceMaxAccel = bRealForceMaxAccel ;
Pawn . bRootMotionFromInterpCurve = bRealRootMotionFromInterpCurve ;
Pawn . Mesh . RootMotionMode = RealRootMotionMode ;
}
}
final function SavedMove GetFreeMove ( )
{
local SavedMove s , first ;
local int i ;
if ( FreeMoves == None )
{
// don't allow more than 100 saved moves
For ( s = SavedMoves ; s != None ; s = s . NextMove )
{
i ++ ;
if ( i > 100 )
{
first = SavedMoves ;
SavedMoves = SavedMoves . NextMove ;
first . Clear ( ) ;
first . NextMove = None ;
// clear out all the moves
While ( SavedMoves != None )
{
s = SavedMoves ;
SavedMoves = SavedMoves . NextMove ;
s . Clear ( ) ;
s . NextMove = FreeMoves ;
FreeMoves = s ;
}
PendingMove = None ;
return first ;
}
}
return new ( self ) SavedMoveClass ;
}
else
{
s = FreeMoves ;
FreeMoves = FreeMoves . NextMove ;
s . NextMove = None ;
return s ;
}
}
function int CompressAccel ( int C )
{
if ( C >= 0 )
C = Min ( C , 127 ) ;
else
C = Min ( abs ( C ) , 127 ) + 128 ;
return C ;
}
/ *
=== === === === === === === === === === === === === === === === === === === === === === === ===
Here ' s how player movement prediction , replication and correction works in network games :
Every tick , the PlayerTick ( ) function is called . It calls the PlayerMove ( ) function ( which is implemented
in various states ) . PlayerMove ( ) figures out the acceleration and rotation , and then calls ProcessMove ( )
( for single player or listen servers ) , or ReplicateMove ( ) ( if its a network client ) .
ReplicateMove ( ) saves the move ( in the PendingMove list ) , calls ProcessMove ( ) , and then replicates the move
to the server by calling the replicated function ServerMove ( ) - passing the movement parameters , the client ' s
resultant position , and a timestamp .
ServerMove ( ) is executed on the server . It decodes the movement parameters and causes the appropriate movement
to occur . It then looks at the resulting position and if enough time has passed since the last response , or the
position error is significant enough , the server calls ClientAdjustPosition ( ) , a replicated function .
ClientAdjustPosition ( ) is executed on the client . The client sets its position to the servers version of position ,
and sets the bUpdatePosition flag to true .
When PlayerTick ( ) is called on the client again , if bUpdatePosition is true , the client will call
ClientUpdatePosition ( ) before calling PlayerMove ( ) . ClientUpdatePosition ( ) replays all the moves in the pending
move list which occured after the timestamp of the move the server was adjusting .
* /
//
// Replicate this client's desired movement to the server.
//
function ReplicateMove
(
float DeltaTime ,
vector NewAccel ,
eDoubleClickDir DoubleClickMove ,
rotator DeltaRot
)
{
local SavedMove NewMove , OldMove , AlmostLastMove , LastMove ;
local byte ClientRoll ;
local float NetMoveDelta ;
// do nothing if we are no longer connected
if ( Player == None )
{
return ;
}
MaxResponseTime = Default . MaxResponseTime * WorldInfo . TimeDilation ;
DeltaTime = ( ( Pawn != None ) ? Pawn . CustomTimeDilation : CustomTimeDilation ) * FMin ( DeltaTime , MaxResponseTime ) ;
// find the most recent move (LastMove), and the oldest (unacknowledged) important move (OldMove)
// a SavedMove is interesting if it differs significantly from the last acknowledged move
if ( SavedMoves != None )
{
LastMove = SavedMoves ;
AlmostLastMove = LastMove ;
OldMove = None ;
while ( LastMove . NextMove != None )
{
// find first important unacknowledged move
if ( ( OldMove == None ) && ( Pawn != None ) && LastMove . IsImportantMove ( LastAckedAccel ) )
{
OldMove = LastMove ;
}
AlmostLastMove = LastMove ;
LastMove = LastMove . NextMove ;
}
}
// Get a SavedMove object to store the movement in.
NewMove = GetFreeMove ( ) ;
if ( NewMove == None )
{
return ;
}
NewMove . SetMoveFor ( self , DeltaTime , NewAccel , DoubleClickMove ) ;
// Simulate the movement locally.
bDoubleJump = false ;
ProcessMove ( NewMove . Delta , NewMove . Acceleration , NewMove . DoubleClickMove , DeltaRot ) ;
// see if the two moves could be combined
if ( ( PendingMove != None ) && PendingMove . CanCombineWith ( NewMove , Pawn , MaxResponseTime ) )
{
// to combine move, first revert pawn position to PendingMove start position, before playing combined move on client
Pawn . SetLocation ( PendingMove . GetStartLocation ( ) ) ;
Pawn . Velocity = PendingMove . StartVelocity ;
if ( PendingMove . StartBase != Pawn . Base )
{
Pawn . SetBase ( PendingMove . StartBase ) ;
}
Pawn . Floor = PendingMove . StartFloor ;
NewMove . Delta += PendingMove . Delta ;
NewMove . SetInitialPosition ( Pawn ) ;
// remove pending move from move list
if ( LastMove == PendingMove )
{
if ( SavedMoves == PendingMove )
{
SavedMoves . NextMove = FreeMoves ;
FreeMoves = SavedMoves ;
SavedMoves = None ;
}
else
{
PendingMove . NextMove = FreeMoves ;
FreeMoves = PendingMove ;
if ( AlmostLastMove != None )
{
AlmostLastMove . NextMove = None ;
LastMove = AlmostLastMove ;
}
}
FreeMoves . Clear ( ) ;
}
PendingMove = None ;
}
if ( Pawn != None )
{
Pawn . AutonomousPhysics ( NewMove . Delta ) ;
}
else
{
AutonomousPhysics ( DeltaTime ) ;
}
NewMove . PostUpdate ( self ) ;
if ( SavedMoves == None )
{
SavedMoves = NewMove ;
}
else
{
LastMove . NextMove = NewMove ;
}
if ( PendingMove == None )
{
// Decide whether to hold off on move
// send moves more frequently in small games where server isn't likely to be saturated
if ( ( Player . CurrentNetSpeed > 10000 ) && ( WorldInfo . GRI != None ) && ( WorldInfo . GRI . PRIArray . Length <= 10 ) )
{
NetMoveDelta = 0.011 ;
}
else
{
NetMoveDelta = FMax ( 0.0222 , 2 * WorldInfo . MoveRepSize / Player . CurrentNetSpeed ) ;
}
if ( ( WorldInfo . TimeSeconds - ClientUpdateTime ) * WorldInfo . TimeDilation < NetMoveDelta )
{
PendingMove = NewMove ;
return ;
}
}
ClientUpdateTime = WorldInfo . TimeSeconds ;
// Send to the server
ClientRoll = ( Rotation . Roll >> 8 ) & 255 ;
CallServerMove ( NewMove ,
( ( Pawn == None ) ? Location : Pawn . Location ) ,
ClientRoll ,
( ( Rotation . Yaw & 65535 ) << 16 ) + ( Rotation . Pitch & 65535 ) ,
OldMove ) ;
PendingMove = None ;
}
/ * C a l l S e r v e r M o v e ( )
Call the appropriate replicated servermove ( ) function to send a client player move to the server
* /
function CallServerMove
(
SavedMove NewMove ,
vector ClientLoc ,
byte ClientRoll ,
int View ,
SavedMove OldMove
)
{
local vector BuildAccel ;
local byte OldAccelX , OldAccelY , OldAccelZ ;
// compress old move if it exists
if ( OldMove != None )
{
// old move important to replicate redundantly
BuildAccel = 0.05 * OldMove . Acceleration + vect ( 0.5 , 0.5 , 0.5 ) ;
OldAccelX = CompressAccel ( BuildAccel . X ) ;
OldAccelY = CompressAccel ( BuildAccel . Y ) ;
OldAccelZ = CompressAccel ( BuildAccel . Z ) ;
OldServerMove ( OldMove . TimeStamp , OldAccelX , OldAccelY , OldAccelZ , OldMove . CompressedFlags ( ) ) ;
}
if ( PendingMove != None )
{
// send two moves simultaneously
DualServerMove
(
PendingMove . TimeStamp ,
PendingMove . Acceleration * 10 ,
PendingMove . CompressedFlags ( ) ,
( ( PendingMove . Rotation . Yaw & 65535 ) << 16 ) + ( PendingMove . Rotation . Pitch & 65535 ) ,
NewMove . TimeStamp ,
NewMove . Acceleration * 10 ,
ClientLoc ,
NewMove . CompressedFlags ( ) ,
ClientRoll ,
View
) ;
}
else
{
ServerMove
(
NewMove . TimeStamp ,
NewMove . Acceleration * 10 ,
ClientLoc ,
NewMove . CompressedFlags ( ) ,
ClientRoll ,
View
) ;
}
if ( PlayerCamera != None && PlayerCamera . bUseClientSideCameraUpdates )
{
PlayerCamera . bShouldSendClientSideCameraUpdate = TRUE ;
}
}
/** If PlayerCamera.bUseClientSideCameraUpdates is set, client will replicate camera positions to the server. */
// @TODO - combine pitch/yaw into one int, maybe also send location compressed
unreliable server function ServerUpdateCamera ( vector CamLoc , int CamPitchAndYaw )
{
local TPOV NewPOV ;
NewPOV . Location = CamLoc ;
NewPOV . Rotation . Yaw = ( CamPitchAndYaw >> 16 ) & 65535 ;
NewPOV . Rotation . Pitch = CamPitchAndYaw & 65535 ;
if ( PlayerCamera . bDebugClientSideCamera )
{
// show differences (on server) between local and replicated camera
DrawDebugSphere ( PlayerCamera . CameraCache . POV . Location , 10 , 10 , 0 , 255 , 0 ) ;
DrawDebugSphere ( NewPOV . Location , 10 , 10 , 255 , 255 , 0 ) ;
DrawDebugLine ( PlayerCamera . CameraCache . POV . Location , PlayerCamera . CameraCache . POV . Location + 100 * vector ( PlayerCamera . CameraCache . POV . Rotation ) , 0 , 255 , 0 ) ;
DrawDebugLine ( NewPOV . Location , NewPOV . Location + 100 * vector ( NewPOV . Rotation ) , 255 , 255 , 0 ) ;
}
else
{
PlayerCamera . FillCameraCache ( NewPOV ) ;
}
}
/ * H a n d l e W a l k i n g :
Called by PlayerController and PlayerInput to set bIsWalking flag , affecting Pawn ' s velocity * /
function HandleWalking ( )
{
if ( Pawn != None )
Pawn . SetWalking ( bRun != 0 ) ;
}
reliable server function ServerRestartGame ( )
{
}
// Send a voice message of a certain type to a certain player.
exec function Speech ( name Type , int Index , string Callsign )
{
ServerSpeech ( Type , Index , Callsign ) ;
}
reliable server function ServerSpeech ( name Type , int Index , string Callsign ) ;
exec function RestartLevel ( )
{
if ( WorldInfo . NetMode == NM _Standalone )
{
ClientTravel ( "?restart" , TRAVEL _Relative ) ;
}
}
exec function LocalTravel ( string URL )
{
if ( WorldInfo . NetMode == NM _Standalone )
ClientTravel ( URL , TRAVEL _Relative ) ;
}
/ * *
* Pause force - feedback for all players .
*
* @ param bShouldPauseRumble indicates whether force - feedback should be paused or unpaused .
* /
function PauseRumbleForAllPlayers ( optional bool bShouldPauseRumble = true )
{
local PlayerController PC ;
foreach WorldInfo . AllControllers ( class 'PlayerController' , PC )
{
PC . ClientPauseRumble ( bShouldPauseRumble ) ;
}
}
/ * *
* Pause force - feedback for this player .
*
* @ param bShouldPauseRumble indicates whether force - feedback should be paused or unpaused .
* /
reliable client function ClientPauseRumble ( bool bShouldPauseRumble )
{
if ( ForceFeedbackManager != None )
{
ForceFeedbackManager . PauseWaveform ( bShouldPauseRumble ) ;
}
}
/** Callback the server uses to determine if the unpause can happen */
delegate bool CanUnpause ( )
{
return WorldInfo . Pauser == PlayerReplicationInfo ;
}
/** Try to pause game; returns success indicator. */
function bool SetPause ( bool bPause , optional delegate < CanUnpause > CanUnpauseDelegate = CanUnpause )
{
local bool bResult ;
if ( WorldInfo . NetMode != NM _Client )
{
if ( bPause )
{
bFire = 0 ;
// Pause gamepad rumbling too if needed
bResult = WorldInfo . Game . SetPause ( self , CanUnpauseDelegate ) ;
if ( bResult )
{
PauseRumbleForAllPlayers ( ) ;
}
}
else
{
WorldInfo . Game . ClearPause ( ) ;
// If the unpause is complete, let rumble occur
if ( WorldInfo . Pauser == None )
{
// If we did a gameplay frame pause clear it out now
WorldInfo . bGameplayFramePause = false ;
PauseRumbleForAllPlayers ( false ) ;
}
}
}
return bResult ;
}
/ * *
* Returns whether the game is currently paused .
* /
final simulated function bool IsPaused ( )
{
return WorldInfo . Pauser != None ;
}
/ * P a u s e ( )
Command to try to pause the game .
* /
exec function Pause ( )
{
ServerPause ( ) ;
}
reliable server function ServerPause ( )
{
// Pause if not already
if ( ! IsPaused ( ) )
SetPause ( true ) ;
else
SetPause ( false ) ;
}
/ * *
* Toggles the game ' s paused state if it does not match the desired pause state .
*
* @ param bDesiredPauseState TRUE indicates that the game should be paused .
* /
event ConditionalPause ( bool bDesiredPauseState )
{
if ( bDesiredPauseState != IsPaused ( ) )
{
SetPause ( bDesiredPauseState ) ;
}
}
reliable server function ServerUTrace ( )
{
if ( WorldInfo . NetMode != NM _Standalone
&& ( PlayerReplicationInfo == None || ! PlayerReplicationInfo . bAdmin ) )
{
return ;
}
UTrace ( ) ;
}
exec function UTrace ( )
{
// disable the log, or we'll grind the game to a halt
ConsoleCommand ( "hidelog" ) ;
if ( Role != ROLE _Authority )
{
ServerUTrace ( ) ;
}
SetUTracing ( ! IsUTracing ( ) ) ;
` log("UTracing changed to " $ IsUTracing() $ " at " $ WorldInfo.TimeSeconds,,'UTrace');
}
// ------------------------------------------------------------------------
// Weapon changing functions
/ * T h r o w W e a p o n ( )
Throw out current weapon , and switch to a new weapon
* /
exec function ThrowWeapon ( )
{
if ( ( Pawn == None ) || ( Pawn . Weapon == None ) )
return ;
ServerThrowWeapon ( ) ;
}
reliable server function ServerThrowWeapon ( )
{
if ( Pawn . CanThrowWeapon ( ) )
{
Pawn . ThrowActiveWeapon ( ) ;
}
}
/ * P r e v W e a p o n ( )
- switch to previous inventory group weapon
* /
exec function PrevWeapon ( )
{
if ( WorldInfo . Pauser != None )
return ;
if ( Pawn . Weapon == None )
{
SwitchToBestWeapon ( ) ;
return ;
}
if ( Pawn . InvManager != None )
Pawn . InvManager . PrevWeapon ( ) ;
}
/ * N e x t W e a p o n ( )
- switch to next inventory group weapon
* /
exec function NextWeapon ( )
{
if ( WorldInfo . Pauser != None )
return ;
if ( Pawn . Weapon == None )
{
SwitchToBestWeapon ( ) ;
return ;
}
if ( Pawn . InvManager != None )
Pawn . InvManager . NextWeapon ( ) ;
}
// The player wants to fire.
exec function StartFire ( optional byte FireModeNum )
{
if ( WorldInfo . Pauser == PlayerReplicationInfo )
{
SetPause ( false ) ;
return ;
}
if ( Pawn != None && ! bCinematicMode && ! WorldInfo . bPlayersOnly )
{
Pawn . StartFire ( FireModeNum ) ;
}
}
exec function StopFire ( optional byte FireModeNum )
{
if ( Pawn != None )
{
Pawn . StopFire ( FireModeNum ) ;
}
}
// The player wants to alternate-fire.
exec function StartAltFire ( optional Byte FireModeNum )
{
StartFire ( 1 ) ;
}
exec function StopAltFire ( optional byte FireModeNum )
{
StopFire ( 1 ) ;
}
/ * *
* Looks at all nearby triggers , looking for any that can be
* interacted with .
*
* @ param interactDistanceToCheck - distance to search for nearby triggers
*
* @ param crosshairDist - distance from the crosshair that
* triggers must be , else they will be filtered out
*
* @ param minDot - minimum dot product between trigger and the
* camera orientation needed to make the list
*
* @ param bUsuableOnly - if true , event must return true from
* SequenceEvent : : CheckActivate ( )
*
* @ param out _useList - the list of triggers found to be
* usuable
* /
function GetTriggerUseList ( float interactDistanceToCheck , float crosshairDist , float minDot , bool bUsuableOnly , out array < Trigger > out _useList )
{
local int Idx ;
local vector cameraLoc ;
local rotator cameraRot ;
local Trigger checkTrigger ;
local SeqEvent _Used UseSeq ;
if ( Pawn != None )
{
// grab camera location/rotation for checking crosshairDist
GetPlayerViewPoint ( cameraLoc , cameraRot ) ;
// This doesn't work how it should. It really needs to query ALL of the triggers and get their
// InteractDistance and then compare those against the pawn's location and then do the various checks
// search of nearby actors that have use events
foreach Pawn . CollidingActors ( class 'Trigger' , checkTrigger , interactDistanceToCheck )
{
for ( Idx = 0 ; Idx < checkTrigger . GeneratedEvents . Length ; Idx ++ )
{
UseSeq = SeqEvent _Used ( checkTrigger . GeneratedEvents [ Idx ] ) ;
if ( ( UseSeq != None )
// if bUsuableOnly is true then we must get true back from CheckActivate (which tests various validity checks on the player and on the trigger's trigger count and retrigger conditions etc)
&& ( ! bUsuableOnly || ( checkTrigger . GeneratedEvents [ Idx ] . CheckActivate ( checkTrigger , Pawn , true ) ) )
// check to see if we are looking at the object
&& ( Normal ( checkTrigger . Location - cameraLoc ) dot vector ( cameraRot ) >= minDot )
// if this is an aimToInteract then check to see if we are aiming at the object and we are inside the InteractDistance (NOTE: we need to do use a number close to 1.0 as the dot will give a number that is very close to 1.0 for aiming at the target)
&& ( ( ( UseSeq . bAimToInteract && IsAimingAt ( checkTrigger , 0.98 f ) && ( VSize ( Pawn . Location - checkTrigger . Location ) <= UseSeq . InteractDistance ) ) )
// if we should NOT aim to interact then we need to be close to the trigger
|| ( ! UseSeq . bAimToInteract && ( VSize ( Pawn . Location - checkTrigger . Location ) <= UseSeq . InteractDistance ) ) // this should be UseSeq.InteractDistance
)
)
{
out _useList [ out _useList . Length ] = checkTrigger ;
// don't bother searching for more events
Idx = checkTrigger . GeneratedEvents . Length ;
}
}
}
}
}
/ * *
* Entry point function for player interactions with the world ,
* re - directs to ServerUse .
* /
exec function Use ( )
{
if ( Role < Role _Authority )
{
PerformedUseAction ( ) ;
}
ServerUse ( ) ;
}
/ * *
* Player pressed UseKey
* /
unreliable server function ServerUse ( )
{
PerformedUseAction ( ) ;
}
/ * *
* return true if player the Use action was handled
* /
function bool PerformedUseAction ( )
{
// if the level is paused,
if ( WorldInfo . Pauser == PlayerReplicationInfo )
{
if ( Role == Role _Authority )
{
// unpause and move on
SetPause ( false ) ;
}
return true ;
}
if ( Pawn == None )
{
return true ;
}
// below is only on server
if ( Role < Role _Authority )
{
return false ;
}
// leave vehicle if currently in one
if ( Vehicle ( Pawn ) != None )
{
return Vehicle ( Pawn ) . DriverLeave ( false ) ;
}
// try to find a vehicle to drive
if ( FindVehicleToDrive ( ) )
{
return true ;
}
// try to interact with triggers
return TriggerInteracted ( ) ;
}
/** Tries to find a vehicle to drive within a limited radius. Returns true if successful */
function bool FindVehicleToDrive ( )
{
local Vehicle V , Best ;
local vector ViewDir , PawnLoc2D , VLoc2D ;
local float NewDot , BestDot ;
if ( Vehicle ( Pawn . Base ) != None && Vehicle ( Pawn . Base ) . TryToDrive ( Pawn ) )
{
return true ;
}
// Pick best nearby vehicle
PawnLoc2D = Pawn . Location ;
PawnLoc2D . Z = 0 ;
ViewDir = vector ( Pawn . Rotation ) ;
ForEach Pawn . OverlappingActors ( class 'Vehicle' , V , Pawn . VehicleCheckRadius )
{
// Prefer vehicles that Pawn is facing
VLoc2D = V . Location ;
Vloc2D . Z = 0 ;
NewDot = Normal ( VLoc2D - PawnLoc2D ) Dot ViewDir ;
if ( ( Best == None ) || ( NewDot > BestDot ) )
{
// check that vehicle is visible
if ( FastTrace ( V . Location , Pawn . Location ) )
{
Best = V ;
BestDot = NewDot ;
}
}
}
return ( Best != None && Best . TryToDrive ( Pawn ) ) ;
}
/ * *
* Examines the nearby enviroment and generates a priority sorted
* list of interactable actors , and then attempts to activate each
* of them until either one was successfully activated , or no more
* actors are available .
* /
function bool TriggerInteracted ( )
{
local Actor A ;
local int Idx ;
local float Weight ;
local bool bInserted ;
local vector cameraLoc ;
local rotator cameraRot ;
local array < Trigger > useList ;
// the following 2 arrays should always match in length
local array < Actor > sortedList ;
local array < float > weightList ;
if ( Pawn != None )
{
GetTriggerUseList ( InteractDistance , 60. f , 0. f , true , useList ) ;
// if we have found some interactable actors,
if ( useList . Length > 0 )
{
// grab the current camera location/rotation for weighting purposes
GetPlayerViewPoint ( cameraLoc , cameraRot ) ;
// then build the sorted list
while ( useList . Length > 0 )
{
// pop the actor off this list
A = useList [ useList . Length - 1 ] ;
useList . Length = useList . Length - 1 ;
// calculate the weight of this actor in terms of optimal interaction
// first based on the dot product from our view rotation
weight = Normal ( A . Location - cameraLoc ) dot vector ( cameraRot ) ;
// and next on the distance
weight += 1. f - ( VSize ( A . Location - Pawn . Location ) / InteractDistance ) ;
// find the optimal insertion point
bInserted = false ;
for ( Idx = 0 ; Idx < sortedList . Length && ! bInserted ; Idx ++ )
{
if ( weightList [ Idx ] < weight )
{
// insert the new entry
sortedList . Insert ( Idx , 1 ) ;
weightList . Insert ( Idx , 1 ) ;
sortedList [ Idx ] = A ;
weightList [ Idx ] = weight ;
bInserted = true ;
}
}
// if no insertion was made
if ( ! bInserted )
{
// then tack on the end of the list
Idx = sortedList . Length ;
sortedList [ Idx ] = A ;
weightList [ Idx ] = weight ;
}
}
// finally iterate through each actor in the sorted list and
// attempt to activate it until one succeeds or none are left
for ( Idx = 0 ; Idx < sortedList . Length ; Idx ++ )
{
if ( sortedList [ Idx ] . UsedBy ( Pawn ) )
{
// skip the rest
// Idx = sortedList.Length;
return true ;
}
}
}
}
return false ;
}
exec function Suicide ( )
{
ServerSuicide ( ) ;
}
reliable server function ServerSuicide ( )
{
if ( ( Pawn != None ) && ( ( WorldInfo . TimeSeconds - Pawn . LastStartTime > 10 ) || ( WorldInfo . NetMode == NM _Standalone ) ) )
{
Pawn . Suicide ( ) ;
}
}
exec function SetName ( coerce string S )
{
local string NewName ;
local LocalPlayer LocPlayer ;
if ( S != "" )
{
LocPlayer = LocalPlayer ( Player ) ;
if ( LocPlayer != None &&
OnlineSub . GameInterface != None &&
OnlineSub . PlayerInterface != None )
{
// Check to see if they are logged in locally or not
if ( OnlineSub . PlayerInterface . GetLoginStatus ( LocPlayer . ControllerId ) == LS _LoggedIn &&
OnlineSub . GameInterface . GetGameSettings ( 'Game' ) != None )
{
// Ignore what ever was specified and use the profile's nick
S = OnlineSub . PlayerInterface . GetPlayerNickname ( LocPlayer . ControllerId ) ;
}
}
NewName = S ;
ServerChangeName ( NewName ) ;
UpdateURL ( "Name" , NewName , true ) ;
SaveConfig ( ) ;
}
}
reliable server function ServerChangeName ( coerce string S )
{
if ( S != "" )
{
WorldInfo . Game . ChangeName ( self , S , true ) ;
}
}
exec function SwitchTeam ( )
{
if ( ( PlayerReplicationInfo . Team == None ) || ( PlayerReplicationInfo . Team . TeamIndex == 1 ) )
{
ServerChangeTeam ( 0 ) ;
}
else
{
ServerChangeTeam ( 1 ) ;
}
}
exec function ChangeTeam ( optional string TeamName )
{
local int N ;
if ( TeamName ~ = "blue" )
N = 1 ;
else if ( ( TeamName ~ = "red" ) || ( PlayerReplicationInfo == None ) || ( PlayerReplicationInfo . Team == None ) || ( PlayerReplicationInfo . Team . TeamIndex > 1 ) )
N = 0 ;
else
N = 1 - PlayerReplicationInfo . Team . TeamIndex ;
ServerChangeTeam ( N ) ;
}
reliable server function ServerChangeTeam ( int N )
{
local TeamInfo OldTeam ;
OldTeam = PlayerReplicationInfo . Team ;
WorldInfo . Game . ChangeTeam ( self , N , true ) ;
if ( WorldInfo . Game . bTeamGame && PlayerReplicationInfo . Team != OldTeam )
{
if ( Pawn != None )
{
Pawn . PlayerChangedTeam ( ) ;
}
}
}
exec function SwitchLevel ( string URL )
{
if ( WorldInfo . NetMode == NM _Standalone || WorldInfo . NetMode == NM _ListenServer )
{
WorldInfo . ServerTravel ( URL ) ;
}
}
/** server to client RPC for server-generated network messages that aren't part of the connection process (e.g. being kicked) */
reliable client event ClientSetProgressMessage ( EProgressMessageType MessageType , string Message , optional string Title , optional bool bIgnoreFutureNetworkMessages )
{
if ( LocalPlayer ( Player ) != None )
{
LocalPlayer ( Player ) . ViewportClient . SetProgressMessage ( MessageType , Message , Title , bIgnoreFutureNetworkMessages ) ;
}
else
{
` Warn("Discarded progress message due to no viewport:" @ MessageType @ Message @ Title);
}
}
function Restart ( bool bVehicleTransition )
{
Super . Restart ( bVehicleTransition ) ;
ServerTimeStamp = 0 ;
ResetTimeMargin ( ) ;
EnterStartState ( ) ;
ClientRestart ( Pawn ) ;
SetViewTarget ( Pawn ) ;
ResetCameraMode ( ) ;
}
/ * * c a l l e d t o n o t i f y t h e s e r v e r w h e n t h e c l i e n t h a s l o a d e d a n e w w o r l d v i a s e a m l e s s t r a v e l l i n g
* @ param WorldPackageName the name of the world package that was loaded
* /
reliable server native final event ServerNotifyLoadedWorld ( name WorldPackageName ) ;
/ * * c a l l e d c l i e n t s i d e w h e n i t i s l o a d e d a n e w w o r l d v i a s e a m l e s s t r a v e l l i n g
* @ param WorldPackageName the name of the world package that was loaded
* @ param bFinalDest whether this world is the destination map for the travel ( i . e . not the transition level )
* /
event NotifyLoadedWorld ( name WorldPackageName , bool bFinalDest )
{
local PlayerStart P ;
local rotator SpawnRotation ;
// place the camera at the first playerstart we can find
SetViewTarget ( self ) ;
foreach WorldInfo . AllNavigationPoints ( class 'PlayerStart' , P )
{
SetLocation ( P . Location ) ;
SpawnRotation . Yaw = P . Rotation . Yaw ;
SetRotation ( SpawnRotation ) ;
break ;
}
}
/** returns whether the client has completely loaded the server's current world (valid on server only) */
native final function bool HasClientLoadedCurrentWorld ( ) ;
/ * * f o r c e s a f u l l r e p l i c a t i o n c h e c k o f t h e s p e c i f i e d A c t o r o n o n l y t h e c l i e n t t h a t o w n s t h i s P l a y e r C o n t r o l l e r
* ( equivalent to setting bForceNetUpdate and bNetDirty to true , but only for that client )
* this function has no effect if this PC is not a remote client or if the Actor is not relevant to that client
* /
native final function ForceSingleNetUpdateFor ( Actor Target ) ;
function EnterStartState ( )
{
local name NewState ;
if ( Pawn . PhysicsVolume . bWaterVolume )
{
if ( Pawn . HeadVolume . bWaterVolume )
{
Pawn . BreathTime = Pawn . UnderWaterTime ;
}
NewState = Pawn . WaterMovementState ;
}
else
{
NewState = Pawn . LandMovementState ;
}
if ( GetStateName ( ) == NewState )
{
BeginState ( NewState ) ;
}
else
{
GotoState ( NewState ) ;
}
}
reliable client function ClientRestart ( Pawn NewPawn )
{
ResetPlayerMovementInput ( ) ;
CleanOutSavedMoves ( ) ; // don't replay moves previous to possession
Pawn = NewPawn ;
if ( ( Pawn != None ) && Pawn . bTearOff )
{
UnPossess ( ) ;
Pawn = None ;
}
AcknowledgePossession ( Pawn ) ;
if ( Pawn == None )
{
GotoState ( 'WaitingForPawn' ) ;
return ;
}
Pawn . ClientRestart ( ) ;
if ( Role < ROLE _Authority )
{
SetViewTarget ( Pawn ) ;
ResetCameraMode ( ) ;
EnterStartState ( ) ;
}
CleanOutSavedMoves ( ) ;
}
/ * e p i c = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
* : : GameHasEnded
*
* Called from game info upon end of the game , used to
* transition to proper state .
*
* === === === === === === === === === === === === === === === === === ==
* /
function GameHasEnded ( optional Actor EndGameFocus , optional bool bIsWinner )
{
// and transition to the game ended state
SetViewTarget ( EndGameFocus ) ;
GotoState ( 'RoundEnded' ) ;
ClientGameEnded ( EndGameFocus , bIsWinner ) ;
}
/ * e p i c = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
* : : ClientGameEnded
*
* Replicated function called by GameHasEnded ( ) .
*
* @ param EndGameFocus - actor to view with camera
* @ param bIsWinner - true if this controller is on winning team
* === === === === === === === === === === === === === === === === === ==
* /
reliable client function ClientGameEnded ( Actor EndGameFocus , bool bIsWinner )
{
SetViewTarget ( EndGameFocus ) ;
GotoState ( 'RoundEnded' ) ;
}
// Just changed to pendingWeapon
function NotifyChangedWeapon ( Weapon PreviousWeapon , Weapon NewWeapon ) ;
/ * *
* PlayerTick is only called if the PlayerController has a PlayerInput object . Therefore , it will not be called on servers for non - locally controlled playercontrollers
* /
event PlayerTick ( float DeltaTime )
{
if ( ! bShortConnectTimeOut )
{
bShortConnectTimeOut = true ;
ServerShortTimeout ( ) ;
}
if ( Pawn != AcknowledgedPawn )
{
if ( Role < ROLE _Authority )
{
// make sure old pawn controller is right
if ( ( AcknowledgedPawn != None ) && ( AcknowledgedPawn . Controller == self ) )
AcknowledgedPawn . Controller = None ;
}
AcknowledgePossession ( Pawn ) ;
}
PlayerInput . PlayerInput ( DeltaTime ) ;
if ( bUpdatePosition )
{
ClientUpdatePosition ( ) ;
}
PlayerMove ( DeltaTime ) ;
AdjustFOV ( DeltaTime ) ;
}
function PlayerMove ( float DeltaTime ) ;
function bool AimingHelp ( bool bInstantHit )
{
return ( WorldInfo . NetMode == NM _Standalone ) && bAimingHelp ;
}
/** The function called when a CameraLookAt action is deactivated from kismet */
event CameraLookAtFinished ( SeqAct _CameraLookAt Action ) ;
/ * *
* Adjusts weapon aiming direction .
* Gives controller a chance to modify the aiming of the pawn . For example aim error , auto aiming , adhesion , AI help ...
* Requested by weapon prior to firing .
*
* @ param W , weapon about to fire
* @ param StartFireLoc , world location of weapon fire start trace , or projectile spawn loc .
* @ param BaseAimRot , original aiming rotation without any modifications .
* /
function Rotator GetAdjustedAimFor ( Weapon W , vector StartFireLoc )
{
local vector FireDir , AimSpot , HitLocation , HitNormal , OldAim , AimOffset ;
local actor BestTarget , HitActor ;
local float bestAim , bestDist ;
local bool bNoZAdjust , bInstantHit ;
local rotator BaseAimRot , AimRot ;
bInstantHit = ( W == None || W . bInstantHit ) ;
BaseAimRot = ( Pawn != None ) ? Pawn . GetBaseAimRotation ( ) : Rotation ;
FireDir = vector ( BaseAimRot ) ;
HitActor = Trace ( HitLocation , HitNormal , StartFireLoc + W . GetTraceRange ( ) * FireDir , StartFireLoc , true ) ;
if ( ( HitActor != None ) && HitActor . bProjTarget )
{
BestTarget = HitActor ;
bNoZAdjust = true ;
OldAim = HitLocation ;
BestDist = VSize ( BestTarget . Location - Pawn . Location ) ;
}
else
{
// adjust aim based on FOV
bestAim = 0.90 ;
if ( AimingHelp ( bInstantHit ) )
{
bestAim = AimHelpDot ( bInstantHit ) ;
}
else if ( bInstantHit )
bestAim = 1.0 ;
BestTarget = PickTarget ( class 'Pawn' , bestAim , bestDist , FireDir , StartFireLoc , W . WeaponRange ) ;
if ( BestTarget == None )
{
return BaseAimRot ;
}
OldAim = StartFireLoc + FireDir * bestDist ;
}
ShotTarget = Pawn ( BestTarget ) ;
if ( ! AimingHelp ( bInstantHit ) )
{
return BaseAimRot ;
}
// aim at target - help with leading also
FireDir = BestTarget . Location - StartFireLoc ;
AimSpot = StartFireLoc + bestDist * Normal ( FireDir ) ;
AimOffset = AimSpot - OldAim ;
if ( ShotTarget != None )
{
// adjust Z of shooter if necessary
if ( bNoZAdjust )
AimSpot . Z = OldAim . Z ;
else if ( AimOffset . Z < 0 )
AimSpot . Z = ShotTarget . Location . Z + 0.4 * ShotTarget . CylinderComponent . CollisionHeight ;
else
AimSpot . Z = ShotTarget . Location . Z - 0.7 * ShotTarget . CylinderComponent . CollisionHeight ;
}
else
AimSpot . Z = OldAim . Z ;
// if not leading, add slight random error ( significant at long distances )
if ( ! bNoZAdjust )
{
AimRot = rotator ( AimSpot - StartFireLoc ) ;
if ( FOVAngle < DefaultFOV - 8 )
AimRot . Yaw = AimRot . Yaw + 200 - Rand ( 400 ) ;
else
AimRot . Yaw = AimRot . Yaw + 375 - Rand ( 750 ) ;
return AimRot ;
}
return rotator ( AimSpot - StartFireLoc ) ;
}
/ * * A i m H e l p D o t ( )
* @ returns the dot product corresponding to the maximum deflection of target for which aiming help should be applied
* /
function float AimHelpDot ( bool bInstantHit )
{
if ( FOVAngle < DefaultFOV - 8 )
return 0.99 ;
if ( bInstantHit )
return 0.97 ;
return 0.93 ;
}
event bool NotifyLanded ( vector HitNormal , Actor FloorActor )
{
return bUpdating ;
}
//=============================================================================
// Player Control
// Player view.
// Compute the rendering viewpoint for the player.
//
/ * * A d j u s t F O V ( )
FOVAngle smoothly interpolates to DesiredFOV
* /
function AdjustFOV ( float DeltaTime )
{
if ( FOVAngle != DesiredFOV )
{
if ( FOVAngle > DesiredFOV )
FOVAngle = FOVAngle - FMax ( 7 , 0.9 * DeltaTime * ( FOVAngle - DesiredFOV ) ) ;
else
FOVAngle = FOVAngle - FMin ( - 7 , 0.9 * DeltaTime * ( FOVAngle - DesiredFOV ) ) ;
if ( Abs ( FOVAngle - DesiredFOV ) <= 10 )
FOVAngle = DesiredFOV ;
}
}
/** returns player's FOV angle */
event float GetFOVAngle ( )
{
return ( PlayerCamera != None ) ? PlayerCamera . GetFOVAngle ( ) : FOVAngle ;
}
/ * * r e t u r n s w h e t h e r t h i s C o n t r o l l e r i s a l o c a l l y c o n t r o l l e d P l a y e r C o n t r o l l e r
* @ note not valid until the Controller is completely spawned ( i . e , unusable in Pre / PostBeginPlay ( ) )
* /
native function bool IsLocalPlayerController ( ) ;
/ * * r e t u r n s w h e t h e r t h i s c o n t r o l l e r i s a l o c a l c o n t r o l l e r .
* @ RETURN true if NM _Standalone , or is local playercontroller
* /
native function bool IsLocalController ( ) ;
native function SetViewTarget ( Actor NewViewTarget , optional ViewTargetTransitionParams TransitionParams ) ;
/** Wrapper to SetViewTarget with useful defaults */
final function SetViewTargetWithBlend ( Actor NewViewTarget , optional float BlendTime = 0.35 , optional EViewTargetBlendFunction BlendFunc = VTBlend _Cubic , optional float BlendExp = 2. f , optional bool bLockOutgoing = FALSE )
{
local ViewTargetTransitionParams TransitionParams ;
TransitionParams . BlendTime = BlendTime ;
TransitionParams . BlendFunction = BlendFunc ;
TransitionParams . BlendExp = BlendExp ;
TransitionParams . bLockOutgoing = bLockOutgoing ;
SetViewTarget ( NewViewTarget , TransitionParams ) ;
}
reliable client event ClientSetViewTarget ( Actor A , optional ViewTargetTransitionParams TransitionParams )
{
if ( ! bClientSimulatingViewTarget )
{
if ( A == None )
{
ServerVerifyViewTarget ( ) ;
}
SetViewTarget ( A , TransitionParams ) ;
}
}
native function Actor GetViewTarget ( ) ;
reliable server function ServerVerifyViewTarget ( )
{
local Actor TheViewTarget ;
TheViewTarget = GetViewTarget ( ) ;
if ( TheViewTarget == Self )
{
return ;
}
ClientSetViewTarget ( TheViewTarget ) ;
}
event SpawnPlayerCamera ( )
{
if ( CameraClass != None )
{
// Associate Camera with PlayerController
PlayerCamera = Spawn ( CameraClass , self ) ;
if ( PlayerCamera != None )
{
PlayerCamera . InitializeFor ( self ) ;
}
else
{
` Log( "Couldn't Spawn Camera Actor for Player!!" );
}
}
else
{
// not having a CameraClass is fine. Another class will handing the "camera" type duties
// usually PlayerController
}
}
/ * *
* Returns Player ' s Point of View
* For the AI this means the Pawn 's ' Eyes ' ViewPoint
* For a Human player , this means the Camera ' s ViewPoint
*
* @ output out _Location , view location of player
* @ output out _rotation , view rotation of player
* /
simulated event GetPlayerViewPoint ( out vector out _Location , out Rotator out _Rotation )
{
local Actor TheViewTarget ;
// sometimes the PlayerCamera can be none and we probably do not want this
// so we will check to see if we have a CameraClass. Having a CameraClass is
// saying: we want a camera so make certain one exists by spawning one
if ( PlayerCamera == None )
{
if ( CameraClass != None )
{
// Associate Camera with PlayerController
PlayerCamera = Spawn ( CameraClass , Self ) ;
if ( PlayerCamera != None )
{
PlayerCamera . InitializeFor ( Self ) ;
}
else
{
` log("Couldn't Spawn Camera Actor for Player!!");
}
}
}
if ( PlayerCamera != None )
{
PlayerCamera . GetCameraViewPoint ( out _Location , out _Rotation ) ;
}
else
{
TheViewTarget = GetViewTarget ( ) ;
if ( TheViewTarget != None )
{
out _Location = TheViewTarget . Location ;
out _Rotation = TheViewTarget . Rotation ;
}
else
{
super . GetPlayerViewPoint ( out _Location , out _Rotation ) ;
}
}
}
/** Updates any camera view shaking that is going on */
function ViewShake ( float DeltaTime ) ;
function UpdateRotation ( float DeltaTime )
{
local Rotator DeltaRot , newRotation , ViewRotation ;
ViewRotation = Rotation ;
if ( Pawn != none )
{
Pawn . SetDesiredRotation ( ViewRotation ) ;
}
// Calculate Delta to be applied on ViewRotation
DeltaRot . Yaw = PlayerInput . aTurn ;
DeltaRot . Pitch = PlayerInput . aLookUp ;
` if( ` _ _TW _ )
ModifyUpdateRotation ( DeltaTime , DeltaRot ) ;
` endif
ProcessViewRotation ( DeltaTime , ViewRotation , DeltaRot ) ;
SetRotation ( ViewRotation ) ;
ViewShake ( deltaTime ) ;
NewRotation = ViewRotation ;
NewRotation . Roll = Rotation . Roll ;
if ( Pawn != None )
Pawn . FaceRotation ( NewRotation , deltatime ) ;
}
` if( ` _ _TW _ )
/** Modify DeltaRot to account for ForceLookAtPawn, AutoTarget, and TargetAdhesion */
function ModifyUpdateRotation ( float DeltaTime , out Rotator DeltaRot ) ;
` endif
/ * *
* Processes the player ' s ViewRotation
* adds delta rot ( player input ) , applies any limits and post - processing
* returns the final ViewRotation set on PlayerController
*
* @ param DeltaTime , time since last frame
* @ param ViewRotation , current player ViewRotation
* @ param DeltaRot , player input added to ViewRotation
* /
function ProcessViewRotation ( float DeltaTime , out Rotator out _ViewRotation , Rotator DeltaRot )
{
if ( PlayerCamera != None )
{
PlayerCamera . ProcessViewRotation ( DeltaTime , out _ViewRotation , DeltaRot ) ;
}
if ( Pawn != None )
{ // Give the Pawn a chance to modify DeltaRot (limit view for ex.)
Pawn . ProcessViewRotation ( DeltaTime , out _ViewRotation , DeltaRot ) ;
}
else
{
// If Pawn doesn't exist, limit view
// Add Delta Rotation
out _ViewRotation += DeltaRot ;
out _ViewRotation = LimitViewRotation ( out _ViewRotation , - 16384 , 16383 ) ;
}
}
/ * *
* Limit the player ' s view rotation . ( Pitch component ) .
* /
event Rotator LimitViewRotation ( Rotator ViewRotation , float ViewPitchMin , float ViewPitchMax )
{
ViewRotation . Pitch = ViewRotation . Pitch & 65535 ;
if ( ViewRotation . Pitch > ViewPitchMax &&
ViewRotation . Pitch < ( 65535 + ViewPitchMin ) )
{
if ( ViewRotation . Pitch < 32768 )
{
ViewRotation . Pitch = ViewPitchMax ;
}
else
{
ViewRotation . Pitch = 65535 + ViewPitchMin ;
}
}
return ViewRotation ;
}
/ * C h e c k J u m p O r D u c k ( )
Called by ProcessMove ( )
handle jump and duck buttons which are pressed
* /
function CheckJumpOrDuck ( )
{
if ( bPressedJump && ( Pawn != None ) )
{
Pawn . DoJump ( bUpdating ) ;
}
}
/ * *
* Allow player controllers to adjust the acceleration in PlayerWalking
*
* @ param NewAccel - the acceleration used by PlayerWalking : : PlayerMove
* /
function AdjustPlayerWalkingMoveAccel ( out vector NewAccel ) ;
// Player movement.
// Player Standing, walking, running, falling.
state PlayerWalking
{
ignores SeePlayer , HearNoise , Bump ;
event NotifyPhysicsVolumeChange ( PhysicsVolume NewVolume )
{
if ( NewVolume . bWaterVolume && Pawn . bCollideWorld )
{
GotoState ( Pawn . WaterMovementState ) ;
}
}
function ProcessMove ( float DeltaTime , vector NewAccel , eDoubleClickDir DoubleClickMove , rotator DeltaRot )
{
if ( Pawn == None )
{
return ;
}
if ( Role == ROLE _Authority )
{
// Update ViewPitch for remote clients
Pawn . SetRemoteViewPitch ( Rotation . Pitch ) ;
}
Pawn . Acceleration = NewAccel ;
CheckJumpOrDuck ( ) ;
}
function PlayerMove ( float DeltaTime )
{
local vector X , Y , Z , NewAccel ;
local eDoubleClickDir DoubleClickMove ;
local rotator OldRotation ;
local bool bSaveJump ;
if ( Pawn == None )
{
GotoState ( 'Dead' ) ;
}
else
{
GetAxes ( Pawn . Rotation , X , Y , Z ) ;
// Update acceleration.
NewAccel = PlayerInput . aForward * X + PlayerInput . aStrafe * Y ;
NewAccel . Z = 0 ;
NewAccel = Pawn . AccelRate * Normal ( NewAccel ) ;
if ( IsLocalPlayerController ( ) )
{
AdjustPlayerWalkingMoveAccel ( NewAccel ) ;
}
DoubleClickMove = PlayerInput . CheckForDoubleClickMove ( DeltaTime / WorldInfo . TimeDilation ) ;
// Update rotation.
OldRotation = Rotation ;
UpdateRotation ( DeltaTime ) ;
bDoubleJump = false ;
if ( bPressedJump && Pawn . CannotJumpNow ( ) )
{
bSaveJump = true ;
bPressedJump = false ;
}
else
{
bSaveJump = false ;
}
if ( Role < ROLE _Authority ) // then save this move and replicate it
{
ReplicateMove ( DeltaTime , NewAccel , DoubleClickMove , OldRotation - Rotation ) ;
}
else
{
ProcessMove ( DeltaTime , NewAccel , DoubleClickMove , OldRotation - Rotation ) ;
}
bPressedJump = bSaveJump ;
}
}
event BeginState ( Name PreviousStateName )
{
DoubleClickDir = DCLICK _None ;
bPressedJump = false ;
GroundPitch = 0 ;
if ( Pawn != None )
{
Pawn . ShouldCrouch ( false ) ;
if ( Pawn . Physics != PHYS _Falling && Pawn . Physics != PHYS _RigidBody ) // FIXME HACK!!!
Pawn . SetPhysics ( Pawn . WalkingPhysics ) ;
}
}
event EndState ( Name NextStateName )
{
GroundPitch = 0 ;
if ( Pawn != None )
{
Pawn . SetRemoteViewPitch ( 0 ) ;
if ( bDuck == 0 )
{
Pawn . ShouldCrouch ( false ) ;
}
}
}
Begin :
}
// player is climbing ladder
state PlayerClimbing
{
ignores SeePlayer , HearNoise , Bump ;
event NotifyPhysicsVolumeChange ( PhysicsVolume NewVolume )
{
if ( NewVolume . bWaterVolume )
{
GotoState ( Pawn . WaterMovementState ) ;
}
else
{
GotoState ( Pawn . LandMovementState ) ;
}
}
function ProcessMove ( float DeltaTime , vector NewAccel , eDoubleClickDir DoubleClickMove , rotator DeltaRot )
{
if ( Pawn == None )
{
return ;
}
if ( Role == ROLE _Authority )
{
// Update ViewPitch for remote clients
Pawn . SetRemoteViewPitch ( Rotation . Pitch ) ;
}
Pawn . Acceleration = NewAccel ;
if ( bPressedJump )
{
Pawn . DoJump ( bUpdating ) ;
if ( Pawn . Physics == PHYS _Falling )
{
GotoState ( Pawn . LandMovementState ) ;
}
}
}
function PlayerMove ( float DeltaTime )
{
local vector X , Y , Z , NewAccel ;
local rotator OldRotation , ViewRotation ;
GetAxes ( Rotation , X , Y , Z ) ;
// Update acceleration.
if ( Pawn . OnLadder != None )
{
NewAccel = PlayerInput . aForward * Pawn . OnLadder . ClimbDir ;
if ( Pawn . OnLadder . bAllowLadderStrafing )
NewAccel += PlayerInput . aStrafe * Y ;
}
else
NewAccel = PlayerInput . aForward * X + PlayerInput . aStrafe * Y ;
NewAccel = Pawn . AccelRate * Normal ( NewAccel ) ;
ViewRotation = Rotation ;
// Update rotation.
SetRotation ( ViewRotation ) ;
OldRotation = Rotation ;
UpdateRotation ( DeltaTime ) ;
if ( Role < ROLE _Authority ) // then save this move and replicate it
ReplicateMove ( DeltaTime , NewAccel , DCLICK _None , OldRotation - Rotation ) ;
else
ProcessMove ( DeltaTime , NewAccel , DCLICK _None , OldRotation - Rotation ) ;
bPressedJump = false ;
}
event BeginState ( Name PreviousStateName )
{
Pawn . ShouldCrouch ( false ) ;
bPressedJump = false ;
}
event EndState ( Name NextStateName )
{
if ( Pawn != None )
{
Pawn . SetRemoteViewPitch ( 0 ) ;
Pawn . ShouldCrouch ( false ) ;
}
}
}
// Player Driving a vehicle.
state PlayerDriving
{
ignores SeePlayer , HearNoise , Bump ;
function ProcessMove ( float DeltaTime , vector NewAccel , eDoubleClickDir DoubleClickMove , rotator DeltaRot ) ;
// Set the throttle, steering etc. for the vehicle based on the input provided
function ProcessDrive ( float InForward , float InStrafe , float InUp , bool InJump )
{
local Vehicle CurrentVehicle ;
CurrentVehicle = Vehicle ( Pawn ) ;
if ( CurrentVehicle != None )
{
//`log("Forward:"@InForward@" Strafe:"@InStrafe@" Up:"@InUp);
bPressedJump = InJump ;
CurrentVehicle . SetInputs ( InForward , - InStrafe , InUp ) ;
CheckJumpOrDuck ( ) ;
}
}
function PlayerMove ( float DeltaTime )
{
// update 'looking' rotation
UpdateRotation ( DeltaTime ) ;
// TODO: Don't send things like aForward and aStrafe for gunners who don't need it
// Only servers can actually do the driving logic.
ProcessDrive ( PlayerInput . RawJoyUp , PlayerInput . RawJoyRight , PlayerInput . aUp , bPressedJump ) ;
if ( Role < ROLE _Authority )
{
ServerDrive ( PlayerInput . RawJoyUp , PlayerInput . RawJoyRight , PlayerInput . aUp , bPressedJump , ( ( Rotation . Yaw & 65535 ) << 16 ) + ( Rotation . Pitch & 65535 ) ) ;
}
bPressedJump = false ;
}
unreliable server function ServerUse ( )
{
local Vehicle CurrentVehicle ;
CurrentVehicle = Vehicle ( Pawn ) ;
CurrentVehicle . DriverLeave ( false ) ;
}
event BeginState ( Name PreviousStateName )
{
CleanOutSavedMoves ( ) ;
}
event EndState ( Name NextStateName )
{
CleanOutSavedMoves ( ) ;
}
}
// Player movement.
// Player Swimming
state PlayerSwimming
{
ignores SeePlayer , HearNoise , Bump ;
event bool NotifyLanded ( vector HitNormal , Actor FloorActor )
{
if ( Pawn . PhysicsVolume . bWaterVolume )
Pawn . SetPhysics ( PHYS _Swimming ) ;
else
GotoState ( Pawn . LandMovementState ) ;
return bUpdating ;
}
event NotifyPhysicsVolumeChange ( PhysicsVolume NewVolume )
{
local actor HitActor ;
local vector HitLocation , HitNormal , Checkpoint ;
local vector X , Y , Z ;
if ( ! Pawn . bCollideActors )
{
GotoState ( Pawn . LandMovementState ) ;
}
if ( Pawn . Physics != PHYS _RigidBody )
{
if ( ! NewVolume . bWaterVolume )
{
Pawn . SetPhysics ( PHYS _Falling ) ;
if ( Pawn . Velocity . Z > 0 )
{
GetAxes ( Rotation , X , Y , Z ) ;
Pawn . bUpAndOut = ( ( X Dot Pawn . Acceleration ) > 0 ) && ( ( Pawn . Acceleration . Z > 0 ) || ( Rotation . Pitch > 2048 ) ) ;
if ( Pawn . bUpAndOut && Pawn . CheckWaterJump ( HitNormal ) ) //check for waterjump
{
Pawn . velocity . Z = Pawn . OutOfWaterZ ; //set here so physics uses this for remainder of tick
GotoState ( Pawn . LandMovementState ) ;
}
else if ( ( Pawn . Velocity . Z > 160 ) || ! Pawn . TouchingWaterVolume ( ) )
GotoState ( Pawn . LandMovementState ) ;
else //check if in deep water
{
Checkpoint = Pawn . Location ;
Checkpoint . Z -= ( Pawn . CylinderComponent . CollisionHeight + 6.0 ) ;
HitActor = Trace ( HitLocation , HitNormal , Checkpoint , Pawn . Location , false ) ;
if ( HitActor != None )
GotoState ( Pawn . LandMovementState ) ;
else
{
SetTimer ( 0.7 , false ) ;
}
}
}
}
else
{
ClearTimer ( ) ;
Pawn . SetPhysics ( PHYS _Swimming ) ;
}
}
else if ( ! NewVolume . bWaterVolume )
{
// if in rigid body, go to appropriate state, but don't modify pawn physics
GotoState ( Pawn . LandMovementState ) ;
}
}
function ProcessMove ( float DeltaTime , vector NewAccel , eDoubleClickDir DoubleClickMove , rotator DeltaRot )
{
Pawn . Acceleration = NewAccel ;
}
function PlayerMove ( float DeltaTime )
{
local rotator oldRotation ;
local vector X , Y , Z , NewAccel ;
if ( Pawn == None )
{
GotoState ( 'Dead' ) ;
}
else
{
GetAxes ( Rotation , X , Y , Z ) ;
NewAccel = PlayerInput . aForward * X + PlayerInput . aStrafe * Y + PlayerInput . aUp * vect ( 0 , 0 , 1 ) ;
NewAccel = Pawn . AccelRate * Normal ( NewAccel ) ;
// Update rotation.
oldRotation = Rotation ;
UpdateRotation ( DeltaTime ) ;
if ( Role < ROLE _Authority ) // then save this move and replicate it
{
ReplicateMove ( DeltaTime , NewAccel , DCLICK _None , OldRotation - Rotation ) ;
}
else
{
ProcessMove ( DeltaTime , NewAccel , DCLICK _None , OldRotation - Rotation ) ;
}
bPressedJump = false ;
}
}
event Timer ( )
{
if ( ! Pawn . PhysicsVolume . bWaterVolume && Role == ROLE _Authority )
{
GotoState ( Pawn . LandMovementState ) ;
}
ClearTimer ( ) ;
}
event BeginState ( Name PreviousStateName )
{
ClearTimer ( ) ;
if ( Pawn . Physics != PHYS _RigidBody )
{
Pawn . SetPhysics ( PHYS _Swimming ) ;
}
}
Begin :
}
state PlayerFlying
{
ignores SeePlayer , HearNoise , Bump ;
function PlayerMove ( float DeltaTime )
{
local vector X , Y , Z ;
GetAxes ( Rotation , X , Y , Z ) ;
Pawn . Acceleration = PlayerInput . aForward * X + PlayerInput . aStrafe * Y + PlayerInput . aUp * vect ( 0 , 0 , 1 ) ; ;
Pawn . Acceleration = Pawn . AccelRate * Normal ( Pawn . Acceleration ) ;
if ( bCheatFlying && ( Pawn . Acceleration == vect ( 0 , 0 , 0 ) ) )
Pawn . Velocity = vect ( 0 , 0 , 0 ) ;
// Update rotation.
UpdateRotation ( DeltaTime ) ;
if ( Role < ROLE _Authority ) // then save this move and replicate it
ReplicateMove ( DeltaTime , Pawn . Acceleration , DCLICK _None , rot ( 0 , 0 , 0 ) ) ;
else
ProcessMove ( DeltaTime , Pawn . Acceleration , DCLICK _None , rot ( 0 , 0 , 0 ) ) ;
}
event BeginState ( Name PreviousStateName )
{
Pawn . SetPhysics ( PHYS _Flying ) ;
}
}
function bool IsSpectating ( )
{
return false ;
}
/** when spectating, tells server where the client is (client is authoritative on location when spectating) */
unreliable server function ServerSetSpectatorLocation ( vector NewLoc )
{
// if we receive this here, the client is in the wrong state; tell it what state it should be in
if ( WorldInfo . TimeSeconds != LastSpectatorStateSynchTime )
{
ClientGotoState ( GetStateName ( ) ) ;
ClientSetViewTarget ( GetViewTarget ( ) ) ;
LastSpectatorStateSynchTime = WorldInfo . TimeSeconds ;
}
}
state BaseSpectating
{
function bool IsSpectating ( )
{
return true ;
}
/ * *
* Adjust spectator velocity if "out of bounds"
* ( above stallz or below killz )
* /
function bool LimitSpectatorVelocity ( )
{
if ( Location . Z > WorldInfo . StallZ )
{
Velocity . Z = FMin ( SpectatorCameraSpeed , WorldInfo . StallZ - Location . Z - 2.0 ) ;
return true ;
}
else if ( Location . Z < WorldInfo . KillZ )
{
Velocity . Z = FMin ( SpectatorCameraSpeed , WorldInfo . KillZ - Location . Z + 2.0 ) ;
return true ;
}
return false ;
}
function ProcessMove ( float DeltaTime , vector NewAccel , eDoubleClickDir DoubleClickMove , rotator DeltaRot )
{
local float VelSize ;
/* smoothly accelerate and decelerate */
Acceleration = Normal ( NewAccel ) * SpectatorCameraSpeed ;
VelSize = VSize ( Velocity ) ;
if ( VelSize > 0 )
{
Velocity = Velocity - ( Velocity - Normal ( Acceleration ) * VelSize ) * FMin ( DeltaTime * 8 , 1 ) ;
}
Velocity = Velocity + Acceleration * DeltaTime ;
if ( VSize ( Velocity ) > SpectatorCameraSpeed )
{
Velocity = Normal ( Velocity ) * SpectatorCameraSpeed ;
}
LimitSpectatorVelocity ( ) ;
if ( VSize ( Velocity ) > 0 )
{
MoveSmooth ( ( 1 + bRun ) * Velocity * DeltaTime ) ;
// correct if out of bounds after move
if ( LimitSpectatorVelocity ( ) )
{
MoveSmooth ( Velocity . Z * vect ( 0 , 0 , 1 ) * DeltaTime ) ;
}
}
}
function PlayerMove ( float DeltaTime )
{
local vector X , Y , Z ;
GetAxes ( Rotation , X , Y , Z ) ;
Acceleration = PlayerInput . aForward * X + PlayerInput . aStrafe * Y + PlayerInput . aUp * vect ( 0 , 0 , 1 ) ;
UpdateRotation ( DeltaTime ) ;
if ( Role < ROLE _Authority ) // then save this move and replicate it
{
ReplicateMove ( DeltaTime , Acceleration , DCLICK _None , rot ( 0 , 0 , 0 ) ) ;
}
else
{
ProcessMove ( DeltaTime , Acceleration , DCLICK _None , rot ( 0 , 0 , 0 ) ) ;
}
}
/** when spectating, tells server where the client is (client is authoritative on location when spectating) */
unreliable server function ServerSetSpectatorLocation ( vector NewLoc )
{
SetLocation ( NewLoc ) ;
if ( WorldInfo . TimeSeconds - LastSpectatorStateSynchTime > 2.0 )
{
ClientGotoState ( GetStateName ( ) ) ;
LastSpectatorStateSynchTime = WorldInfo . TimeSeconds ;
}
}
function ReplicateMove ( float DeltaTime , vector NewAccel , eDoubleClickDir DoubleClickMove , rotator DeltaRot )
{
ProcessMove ( DeltaTime , NewAccel , DoubleClickMove , DeltaRot ) ;
// when spectating, client position is authoritative
ServerSetSpectatorLocation ( Location ) ;
if ( PlayerCamera != None && PlayerCamera . bUseClientSideCameraUpdates )
{
PlayerCamera . bShouldSendClientSideCameraUpdate = TRUE ;
}
}
event BeginState ( Name PreviousStateName )
{
bCollideWorld = true ;
}
event EndState ( Name NextStateName )
{
bCollideWorld = false ;
}
}
unreliable server function ServerViewNextPlayer ( )
{
if ( IsSpectating ( ) )
{
ViewAPlayer ( + 1 ) ;
}
}
unreliable server function ServerViewPrevPlayer ( )
{
if ( IsSpectating ( ) )
{
ViewAPlayer ( - 1 ) ;
}
}
/ * *
* Get next active viewable player in PRIArray .
* @ param dir is the direction to go in the array
* /
function PlayerReplicationInfo GetNextViewablePlayer ( int dir )
{
local int i , CurrentIndex , NewIndex ;
local PlayerReplicationInfo PRI ;
CurrentIndex = - 1 ;
if ( RealViewTarget != None )
{
// Find index of current viewtarget's PRI
For ( i = 0 ; i < WorldInfo . GRI . PRIArray . Length ; i ++ )
{
if ( RealViewTarget == WorldInfo . GRI . PRIArray [ i ] )
{
CurrentIndex = i ;
break ;
}
}
}
// Find next valid viewtarget in appropriate direction
for ( NewIndex = CurrentIndex + dir ; ( NewIndex >= 0 ) && ( NewIndex < WorldInfo . GRI . PRIArray . Length ) ; NewIndex = NewIndex + dir )
{
PRI = WorldInfo . GRI . PRIArray [ NewIndex ] ;
if ( ( PRI != None ) && ( Controller ( PRI . Owner ) != None ) && ( Controller ( PRI . Owner ) . Pawn != None )
&& WorldInfo . Game . CanSpectate ( self , PRI ) )
{
return PRI ;
}
}
// wrap around
CurrentIndex = ( NewIndex < 0 ) ? WorldInfo . GRI . PRIArray . Length : - 1 ;
for ( NewIndex = CurrentIndex + dir ; ( NewIndex >= 0 ) && ( NewIndex < WorldInfo . GRI . PRIArray . Length ) ; NewIndex = NewIndex + dir )
{
PRI = WorldInfo . GRI . PRIArray [ NewIndex ] ;
if ( ( PRI != None ) && ( Controller ( PRI . Owner ) != None ) && ( Controller ( PRI . Owner ) . Pawn != None ) &&
WorldInfo . Game . CanSpectate ( self , PRI ) )
{
return PRI ;
}
}
return None ;
}
/ * *
* View next active player in PRIArray .
* @ param dir is the direction to go in the array
* /
function ViewAPlayer ( int dir )
{
local PlayerReplicationInfo PRI ;
PRI = GetNextViewablePlayer ( dir ) ;
if ( PRI != None )
{
SetViewTarget ( PRI ) ;
}
}
unreliable server function ServerViewSelf ( optional ViewTargetTransitionParams TransitionParams )
{
if ( IsSpectating ( ) )
{
ResetCameraMode ( ) ;
SetViewTarget ( Self , TransitionParams ) ;
ClientSetViewTarget ( Self , TransitionParams ) ;
}
}
state Spectating extends BaseSpectating
{
ignores RestartLevel , Suicide , ThrowWeapon , NotifyPhysicsVolumeChange , NotifyHeadVolumeChange ;
exec function StartFire ( optional byte FireModeNum )
{
ServerViewNextPlayer ( ) ;
}
// Return to spectator's own camera.
exec function StartAltFire ( optional byte FireModeNum )
{
ResetCameraMode ( ) ;
ServerViewSelf ( ) ;
}
event BeginState ( Name PreviousStateName )
{
if ( Pawn != None )
{
SetLocation ( Pawn . Location ) ;
UnPossess ( ) ;
}
bCollideWorld = true ;
}
event EndState ( Name NextStateName )
{
if ( PlayerReplicationInfo != None )
{
if ( PlayerReplicationInfo . bOnlySpectator )
{
` log("WARNING - Spectator only player leaving spectating state to go to " $ NextStateName);
}
PlayerReplicationInfo . bIsSpectator = false ;
}
bCollideWorld = false ;
}
}
auto state PlayerWaiting extends BaseSpectating
{
ignores SeePlayer , HearNoise , NotifyBump , TakeDamage , PhysicsVolumeChange , NextWeapon , PrevWeapon , SwitchToBestWeapon ;
exec function Jump ( ) ;
exec function Suicide ( ) ;
reliable server function ServerSuicide ( ) ;
reliable server function ServerChangeTeam ( int N )
{
WorldInfo . Game . ChangeTeam ( self , N , true ) ;
}
reliable server function ServerRestartPlayer ( )
{
if ( WorldInfo . TimeSeconds < WaitDelay )
return ;
if ( WorldInfo . NetMode == NM _Client )
return ;
if ( WorldInfo . Game . bWaitingToStartMatch )
PlayerReplicationInfo . bReadyToPlay = true ;
else
WorldInfo . Game . RestartPlayer ( self ) ;
}
exec function StartFire ( optional byte FireModeNum )
{
ServerReStartPlayer ( ) ;
}
event EndState ( Name NextStateName )
{
if ( PlayerReplicationInfo != None )
{
PlayerReplicationInfo . SetWaitingPlayer ( false ) ;
}
bCollideWorld = false ;
}
// @note: this must be simulated to execute on the client because at the time the initial state is entered, RemoteRole has not been
// set yet and so only simulated functions will be executed
simulated event BeginState ( Name PreviousStateName )
{
if ( PlayerReplicationInfo != None )
{
PlayerReplicationInfo . SetWaitingPlayer ( true ) ;
}
bCollideWorld = true ;
}
}
state WaitingForPawn extends BaseSpectating
{
ignores SeePlayer , HearNoise , KilledBy ;
exec function StartFire ( optional byte FireModeNum )
{
AskForPawn ( ) ;
}
reliable client function ClientGotoState ( name NewState , optional name NewLabel )
{
if ( NewState == 'RoundEnded' )
{
Global . ClientGotoState ( NewState , NewLabel ) ;
}
}
unreliable client function LongClientAdjustPosition
(
float TimeStamp ,
name newState ,
EPhysics newPhysics ,
float NewLocX ,
float NewLocY ,
float NewLocZ ,
float NewVelX ,
float NewVelY ,
float NewVelZ ,
Actor NewBase ,
float NewFloorX ,
float NewFloorY ,
float NewFloorZ
)
{
if ( newState == 'RoundEnded' )
GotoState ( newState ) ;
}
event PlayerTick ( float DeltaTime )
{
Global . PlayerTick ( DeltaTime ) ;
if ( Pawn != None )
{
Pawn . Controller = self ;
Pawn . BecomeViewTarget ( self ) ;
ClientRestart ( Pawn ) ;
}
else if ( ! IsTimerActive ( ) || GetTimerCount ( ) > 1. f )
{
SetTimer ( 0.2 , true ) ;
AskForPawn ( ) ;
}
}
function ReplicateMove ( float DeltaTime , vector NewAccel , eDoubleClickDir DoubleClickMove , rotator DeltaRot )
{
ProcessMove ( DeltaTime , NewAccel , DoubleClickMove , DeltaRot ) ;
// do not actually call ServerSetSpectatorLocation() as the server is not in this state and so won't accept it anyway
// something is wrong if we're in this state for an extended period of time, so the fact that
// the server side spectator location is not being updated should not be relevant
}
event Timer ( )
{
AskForPawn ( ) ;
}
event BeginState ( Name PreviousStateName )
{
SetTimer ( 0.2 , true ) ;
AskForPawn ( ) ;
}
event EndState ( Name NextStateName )
{
ResetCameraMode ( ) ;
SetTimer ( 0.0 , false ) ;
}
}
state RoundEnded
{
ignores SeePlayer , HearNoise , KilledBy , NotifyBump , HitWall , NotifyHeadVolumeChange , NotifyPhysicsVolumeChange , Falling , TakeDamage , Suicide ;
reliable server function ServerReStartPlayer ( )
{
}
function bool IsSpectating ( )
{
return true ;
}
exec function ThrowWeapon ( ) { }
exec function Use ( ) { }
event Possess ( Pawn aPawn , bool bVehicleTransition )
{
Global . Possess ( aPawn , bVehicleTransition ) ;
if ( Pawn != None )
Pawn . TurnOff ( ) ;
}
reliable server function ServerReStartGame ( )
{
if ( WorldInfo . Game . PlayerCanRestartGame ( self ) )
{
WorldInfo . Game . ResetLevel ( ) ;
}
}
exec function StartFire ( optional byte FireModeNum )
{
if ( Role < ROLE _Authority )
return ;
if ( ! bFrozen )
ServerReStartGame ( ) ;
else if ( ! IsTimerActive ( ) )
SetTimer ( 1.5 , false ) ;
}
function PlayerMove ( float DeltaTime )
{
local vector X , Y , Z ;
local Rotator DeltaRot , ViewRotation ;
GetAxes ( Rotation , X , Y , Z ) ;
// Update view rotation.
ViewRotation = Rotation ;
// Calculate Delta to be applied on ViewRotation
DeltaRot . Yaw = PlayerInput . aTurn ;
DeltaRot . Pitch = PlayerInput . aLookUp ;
ProcessViewRotation ( DeltaTime , ViewRotation , DeltaRot ) ;
SetRotation ( ViewRotation ) ;
ViewShake ( DeltaTime ) ;
if ( Role < ROLE _Authority ) // then save this move and replicate it
ReplicateMove ( DeltaTime , vect ( 0 , 0 , 0 ) , DCLICK _None , rot ( 0 , 0 , 0 ) ) ;
else
ProcessMove ( DeltaTime , vect ( 0 , 0 , 0 ) , DCLICK _None , rot ( 0 , 0 , 0 ) ) ;
bPressedJump = false ;
}
unreliable server function ServerMove
(
float TimeStamp ,
vector InAccel ,
vector ClientLoc ,
byte NewFlags ,
byte ClientRoll ,
` if( ` _ _TW _ )
int View ,
optional int FreeAimRot
` else
int View
` endif
)
{
Global . ServerMove ( TimeStamp ,
InAccel ,
ClientLoc ,
NewFlags ,
ClientRoll ,
//epic superville: Cleaner compression with no roundoff error
( ( Rotation . Yaw & 65535 ) << 16 ) + ( Rotation . Pitch & 65535 )
) ;
}
function FindGoodView ( )
{
local rotator GoodRotation ;
GoodRotation = Rotation ;
GetViewTarget ( ) . FindGoodEndView ( self , GoodRotation ) ;
SetRotation ( GoodRotation ) ;
}
event Timer ( )
{
bFrozen = false ;
}
unreliable client function LongClientAdjustPosition
(
float TimeStamp ,
name newState ,
EPhysics newPhysics ,
float NewLocX ,
float NewLocY ,
float NewLocZ ,
float NewVelX ,
float NewVelY ,
float NewVelZ ,
Actor NewBase ,
float NewFloorX ,
float NewFloorY ,
float NewFloorZ
)
{
}
event BeginState ( Name PreviousStateName )
{
local Pawn P ;
FOVAngle = DesiredFOV ;
bFire = 0 ;
if ( Pawn != None )
{
Pawn . TurnOff ( ) ;
StopFiring ( ) ;
}
if ( myHUD != None )
{
myHUD . SetShowScores ( TRUE ) ;
}
bFrozen = TRUE ;
FindGoodView ( ) ;
SetTimer ( 5 , FALSE ) ;
ForEach DynamicActors ( class 'Pawn' , P )
{
P . TurnOff ( ) ;
}
}
event EndState ( name NextStateName )
{
if ( myHUD != None )
{
myHUD . SetShowScores ( false ) ;
}
}
Begin :
}
state Dead
{
ignores SeePlayer , HearNoise , KilledBy , NextWeapon , PrevWeapon ;
simulated event ReplicatedEvent ( name VarName )
{
// if we got a Pawn, get into the correct control state
// probably should be in global ReplicatedEvent() but minimizing risk here
if ( VarName == nameof ( Pawn ) && Pawn != None && Pawn != AcknowledgedPawn )
{
ClientRestart ( Pawn ) ;
}
Global . ReplicatedEvent ( VarName ) ;
}
exec function ThrowWeapon ( )
{
//clientmessage("Throwweapon while dead, pawn "$Pawn$" health "$Pawn.health);
}
function bool IsDead ( )
{
return true ;
}
reliable server function ServerReStartPlayer ( )
{
if ( ! WorldInfo . Game . PlayerCanRestart ( Self ) )
return ;
super . ServerRestartPlayer ( ) ;
}
exec function StartFire ( optional byte FireModeNum )
{
if ( bFrozen )
{
if ( ! IsTimerActive ( ) || GetTimerCount ( ) > MinRespawnDelay )
bFrozen = false ;
return ;
}
ServerReStartPlayer ( ) ;
}
exec function Use ( )
{
StartFire ( 0 ) ;
}
exec function Jump ( )
{
StartFire ( 0 ) ;
}
unreliable server function ServerMove
(
float TimeStamp ,
vector Accel ,
vector ClientLoc ,
byte NewFlags ,
byte ClientRoll ,
` if( ` _ _TW _ )
int View ,
optional int FreeAimRot
` else
int View
` endif
)
{
Global . ServerMove (
TimeStamp ,
Accel ,
ClientLoc ,
0 ,
ClientRoll ,
View ) ;
}
function PlayerMove ( float DeltaTime )
{
local vector X , Y , Z ;
local rotator DeltaRot , ViewRotation ;
if ( ! bFrozen )
{
if ( bPressedJump )
{
StartFire ( 0 ) ;
bPressedJump = false ;
}
GetAxes ( Rotation , X , Y , Z ) ;
// Update view rotation.
ViewRotation = Rotation ;
// Calculate Delta to be applied on ViewRotation
DeltaRot . Yaw = PlayerInput . aTurn ;
DeltaRot . Pitch = PlayerInput . aLookUp ;
ProcessViewRotation ( DeltaTime , ViewRotation , DeltaRot ) ;
SetRotation ( ViewRotation ) ;
if ( Role < ROLE _Authority ) // then save this move and replicate it
ReplicateMove ( DeltaTime , vect ( 0 , 0 , 0 ) , DCLICK _None , rot ( 0 , 0 , 0 ) ) ;
}
else if ( ! IsTimerActive ( ) || GetTimerCount ( ) > MinRespawnDelay )
{
bFrozen = false ;
}
ViewShake ( DeltaTime ) ;
}
function FindGoodView ( )
{
local vector cameraLoc ;
local rotator cameraRot , ViewRotation ;
local int tries , besttry ;
local float bestdist , newdist ;
local int startYaw ;
local Actor TheViewTarget ;
ViewRotation = Rotation ;
ViewRotation . Pitch = 56000 ;
tries = 0 ;
besttry = 0 ;
bestdist = 0.0 ;
startYaw = ViewRotation . Yaw ;
TheViewTarget = GetViewTarget ( ) ;
for ( tries = 0 ; tries < 16 ; tries ++ )
{
cameraLoc = TheViewTarget . Location ;
SetRotation ( ViewRotation ) ;
GetPlayerViewPoint ( cameraLoc , cameraRot ) ;
newdist = VSize ( cameraLoc - TheViewTarget . Location ) ;
if ( newdist > bestdist )
{
bestdist = newdist ;
besttry = tries ;
}
ViewRotation . Yaw += 4096 ;
}
ViewRotation . Yaw = startYaw + besttry * 4096 ;
SetRotation ( ViewRotation ) ;
}
event Timer ( )
{
if ( ! bFrozen )
return ;
bFrozen = false ;
bPressedJump = false ;
}
event BeginState ( Name PreviousStateName )
{
if ( ( Pawn != None ) && ( Pawn . Controller == self ) )
Pawn . Controller = None ;
Pawn = None ;
FOVAngle = DesiredFOV ;
Enemy = None ;
bFrozen = true ;
bPressedJump = false ;
FindGoodView ( ) ;
SetTimer ( MinRespawnDelay , false ) ;
CleanOutSavedMoves ( ) ;
}
event EndState ( Name NextStateName )
{
CleanOutSavedMoves ( ) ;
Velocity = vect ( 0 , 0 , 0 ) ;
Acceleration = vect ( 0 , 0 , 0 ) ;
if ( ! PlayerReplicationInfo . bOutOfLives )
ResetCameraMode ( ) ;
bPressedJump = false ;
if ( myHUD != None )
{
myHUD . SetShowScores ( false ) ;
}
}
Begin :
if ( LocalPlayer ( Player ) != None )
{
if ( myHUD != None )
{
myHUD . PlayerOwnerDied ( ) ;
}
}
}
function bool CanRestartPlayer ( )
{
return PlayerReplicationInfo != None && ! PlayerReplicationInfo . bOnlySpectator && HasClientLoadedCurrentWorld ( ) && PendingSwapConnection == None ;
}
/ * *
* Hook called from HUD actor . Gives access to HUD and Canvas
* /
function DrawHUD ( HUD H )
{
if ( Pawn != None )
{
Pawn . DrawHUD ( H ) ;
}
if ( PlayerInput != None )
{
PlayerInput . DrawHUD ( H ) ;
}
}
/ * *
* Get a chance to adjust the viewport size before the HUD gets rendered .
* e . g . This allows you to render HUD elements outside the cinematic black bars
*
* X , Y , SizeX , SizeY - these are already set to the viewport size . Adjust as desired .
* /
event AdjustHUDRenderSize ( out int X , out int Y , out int SizeX , out int SizeY , const int FullScreenSizeX , const int FullScreenSizeY )
{
local LocalPlayer LP ;
` if( ` _ _TW _ )
if ( MyHUD != none && MyHUD . bRenderFullScreen )
` else
if ( MyHUD . bRenderFullScreen )
` endif
{
// Use the full render target (ignores splitscreen quadrants and black bars in cinematic mode)
X = 0 ;
Y = 0 ;
SizeX = FullScreenSizeX ;
SizeY = FullScreenSizeY ;
}
` if( ` _ _TW _ )
else if ( MyHUD == none || ! MyHUD . bScaleCanvasForCinematicMode )
` else
else if ( ! MyHUD . bScaleCanvasForCinematicMode )
` endif
{
// Set the canvas size back to the full extents defined by the splitscreen mode, ignoring the scaling due to the cinematic black bars
LP = LocalPlayer ( Player ) ;
if ( LP != None && LP . ViewportClient != None )
{
X = LP . Origin . X * FullScreenSizeX ;
Y = LP . Origin . Y * FullScreenSizeY ;
SizeX = LP . Size . X * FullScreenSizeX ;
SizeY = LP . Size . Y * FullScreenSizeY ;
}
}
}
/ * e p i c = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
* : : OnToggleInput
*
* Looks at the activated input from the SeqAct _ToggleInput
* op and sets bPlayerInputEnabled accordingly .
*
* === === === === === === === === === === === === === === === === === ==
* /
function OnToggleInput ( SeqAct _ToggleInput inAction )
{
local bool bNewValue ;
if ( Role < ROLE _Authority )
{
` Warn("Not supported on client");
return ;
}
if ( inAction . InputLinks [ 0 ] . bHasImpulse )
{
if ( inAction . bToggleMovement )
{
IgnoreMoveInput ( FALSE ) ;
ClientIgnoreMoveInput ( false ) ;
}
if ( inAction . bToggleTurning )
{
IgnoreLookInput ( FALSE ) ;
ClientIgnoreLookInput ( false ) ;
}
}
else
if ( inAction . InputLinks [ 1 ] . bHasImpulse )
{
if ( inAction . bToggleMovement )
{
IgnoreMoveInput ( TRUE ) ;
ClientIgnoreMoveInput ( true ) ;
}
if ( inAction . bToggleTurning )
{
IgnoreLookInput ( TRUE ) ;
ClientIgnoreLookInput ( true ) ;
}
}
else
if ( inAction . InputLinks [ 2 ] . bHasImpulse )
{
if ( inAction . bToggleMovement )
{
bNewValue = ! IsMoveInputIgnored ( ) ;
IgnoreMoveInput ( bNewValue ) ;
ClientIgnoreMoveInput ( bNewValue ) ;
}
if ( inAction . bToggleTurning )
{
bNewValue = ! IsLookInputIgnored ( ) ;
IgnoreLookInput ( bNewValue ) ;
ClientIgnoreLookInput ( bNewValue ) ;
}
}
}
/** calls IgnoreMoveInput on client */
client reliable function ClientIgnoreMoveInput ( bool bIgnore )
{
IgnoreMoveInput ( bIgnore ) ;
}
/** calls IgnoreLookInput on client */
client reliable function ClientIgnoreLookInput ( bool bIgnore )
{
IgnoreLookInput ( bIgnore ) ;
}
/ * *
* list important PlayerController 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 )
{
super . DisplayDebug ( HUD , out _YL , out _YPos ) ;
if ( HUD . ShouldDisplayDebug ( 'camera' ) )
{
if ( PlayerCamera != None )
{
PlayerCamera . DisplayDebug ( HUD , out _YL , out _YPos ) ;
}
else
{
HUD . Canvas . SetDrawColor ( 255 , 0 , 0 ) ;
HUD . Canvas . DrawText ( "NO CAMERA" ) ;
out _YPos += out _YL ;
HUD . Canvas . SetPos ( 4 , out _YPos ) ;
}
}
if ( HUD . ShouldDisplayDebug ( 'input' ) )
{
HUD . Canvas . SetDrawColor ( 255 , 0 , 0 ) ;
HUD . Canvas . DrawText ( "Input ignoremove " $bIgnoreMoveInput$ " ignore look " $bIgnoreLookInput$ " aForward " $PlayerInput . aForward ) ;
out _YPos += out _YL ;
HUD . Canvas . SetPos ( 4 , out _YPos ) ;
}
}
/ * e p i c = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
* : : OnDrawText
*
* Displays text according to parameters set in inAction
*
* === === === === === === === === === === === === === === === === === ==
* /
function OnDrawText ( SeqAct _DrawText inAction )
{
if ( inAction . InputLinks [ 0 ] . bHasImpulse )
{
ClientDrawKismetText ( inAction . DrawTextInfo , inAction . DisplayTimeSeconds ) ;
}
else
{
ClientClearKismetText ( inAction . DrawTextInfo . MessageOffset ) ;
}
}
/** Start drawing a kismet message on the HUD */
reliable client final function ClientDrawKismetText ( KismetDrawTextInfo DrawTextInfo , float DisplayTime )
{
if ( ! bShowKismetDrawText )
{
return ;
}
if ( DisplayTime > 0 )
{
DrawTextInfo . MessageEndTime = WorldInfo . TimeSeconds + DisplayTime ;
}
else
{
DrawTextInfo . MessageEndTime = - 1 ;
}
myHUD . KismetTextInfo . AddItem ( DrawTextInfo ) ;
}
/** Stop drawing a kismet message on the HUD */
reliable client final function ClientClearKismetText ( Vector2D MessageOffset )
{
local int RemoveIdx ;
RemoveIdx = myHUD . KismetTextInfo . Find ( 'MessageOffset' , MessageOffset ) ;
if ( RemoveIdx != INDEX _NONE )
{
myHUD . KismetTextInfo . Remove ( RemoveIdx , 1 ) ;
}
}
/ * e p i c = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
* : : OnSetCameraTarget
*
* Sets the specified view target .
*
* === === === === === === === === === === === === === === === === === ==
* /
simulated function OnSetCameraTarget ( SeqAct _SetCameraTarget inAction )
{
local Actor RealCameraTarget ;
RealCameraTarget = inAction . CameraTarget ;
if ( RealCameraTarget == None )
{
RealCameraTarget = ( Pawn != None ) ? Pawn : self ;
}
// If we're asked to view a Controller, set its Pawn as out view target instead.
else if ( RealCameraTarget . IsA ( 'Controller' ) )
{
RealCameraTarget = Controller ( RealCameraTarget ) . Pawn ;
}
SetViewTarget ( RealCameraTarget , inAction . TransitionParams ) ;
}
simulated function OnToggleHUD ( SeqAct _ToggleHUD inAction )
{
if ( myHUD != None )
{
if ( inAction . InputLinks [ 0 ] . bHasImpulse )
{
myHUD . bShowHUD = true ;
}
else
if ( inAction . InputLinks [ 1 ] . bHasImpulse )
{
myHUD . bShowHUD = false ;
}
else
if ( inAction . InputLinks [ 2 ] . bHasImpulse )
{
myHUD . bShowHUD = ! myHUD . bShowHUD ;
}
}
}
/ * *
* Attempts to match the name passed in to a SeqEvent _Console
* object and then activate it .
*
* @ param eventName - name of the event to cause
* /
unreliable server function ServerCauseEvent ( Name EventName )
{
local array < SequenceObject > AllConsoleEvents ;
local SeqEvent _Console ConsoleEvt ;
local Sequence GameSeq ;
local int Idx ;
local bool bFoundEvt ;
//@TWI_BEGIN - Don't allow ce benchmark on shipping builds
` if( ` isdefined ( ShippingPC ) )
if ( EventName == 'Benchmark' )
{
return ;
}
` endif
//@TWI_END
// Get the gameplay sequence.
GameSeq = WorldInfo . GetGameSequence ( ) ;
if ( ( GameSeq != None ) && ( EventName != '' ) )
{
// Find all SeqEvent_Console objects anywhere.
GameSeq . FindSeqObjectsByClass ( class 'SeqEvent_Console' , TRUE , AllConsoleEvents ) ;
// Iterate over them, seeing if the name is the one we typed in.
for ( Idx = 0 ; Idx < AllConsoleEvents . Length ; Idx ++ )
{
ConsoleEvt = SeqEvent _Console ( AllConsoleEvents [ Idx ] ) ;
if ( ConsoleEvt != None &&
EventName == ConsoleEvt . ConsoleEventName )
{
bFoundEvt = TRUE ;
// activate the vent
ConsoleEvt . CheckActivate ( self , Pawn ) ;
}
}
}
if ( ! bFoundEvt )
{
ListConsoleEvents ( ) ;
}
}
exec function CauseEvent ( optional Name EventName )
{
ServerCauseEvent ( EventName ) ;
}
/ * *
* Shortcut version for LDs who get tired of typing 'CauseEvent' all day . : - )
* /
exec function CE ( optional Name EventName )
{
ServerCauseEvent ( EventName ) ;
}
/ * *
* Lists all console events to the HUD .
* /
exec function ListConsoleEvents ( )
{
local array < SequenceObject > ConsoleEvents ;
local SeqEvent _Console ConsoleEvt ;
local Sequence GameSeq ;
local int Idx ;
` if( ` _ _TW _ )
` if( ` notdefined ( ShippingPC ) )
GameSeq = WorldInfo . GetGameSequence ( ) ;
if ( GameSeq != None )
{
` log("Console events:");
ClientMessage ( "Console events:" , , 15. f ) ;
GameSeq . FindSeqObjectsByClass ( class 'SeqEvent_Console' , TRUE , ConsoleEvents ) ;
for ( Idx = 0 ; Idx < ConsoleEvents . Length ; Idx ++ )
{
ConsoleEvt = SeqEvent _Console ( ConsoleEvents [ Idx ] ) ;
if ( ConsoleEvt != None &&
ConsoleEvt . bEnabled )
{
` log("-"@ConsoleEvt.ConsoleEventName@ConsoleEvt.EventDesc);
ClientMessage ( "-" @ ConsoleEvt . ConsoleEventName @ ConsoleEvt . EventDesc , , 15. f ) ;
}
}
}
` endif
` else
GameSeq = WorldInfo . GetGameSequence ( ) ;
if ( GameSeq != None )
{
` log("Console events:");
ClientMessage ( "Console events:" , , 15. f ) ;
GameSeq . FindSeqObjectsByClass ( class 'SeqEvent_Console' , TRUE , ConsoleEvents ) ;
for ( Idx = 0 ; Idx < ConsoleEvents . Length ; Idx ++ )
{
ConsoleEvt = SeqEvent _Console ( ConsoleEvents [ Idx ] ) ;
if ( ConsoleEvt != None &&
ConsoleEvt . bEnabled )
{
` log("-"@ConsoleEvt.ConsoleEventName@ConsoleEvt.EventDesc);
ClientMessage ( "-" @ ConsoleEvt . ConsoleEventName @ ConsoleEvt . EventDesc , , 15. f ) ;
}
}
}
` endif
}
exec function ListCE ( )
{
ListConsoleEvents ( ) ;
}
/** triggers a SeqEvent_RemoteEvent instead of a console event; LDs specifically requested separate commands */
exec function RemoteEvent ( optional name EventName )
{
ServerRemoteEvent ( EventName ) ;
}
exec function RE ( optional name EventName )
{
ServerRemoteEvent ( EventName ) ;
}
unreliable server function ServerRemoteEvent ( name EventName )
{
local array < SequenceObject > AllRemoteEvents ;
local SeqEvent _RemoteEvent RemoteEvt ;
local Sequence GameSeq ;
local int Idx ;
local bool bFoundEvt ;
// Get the gameplay sequence.
GameSeq = WorldInfo . GetGameSequence ( ) ;
if ( GameSeq != None )
{
// Find all SeqEvent_Console objects anywhere.
GameSeq . FindSeqObjectsByClass ( class 'SeqEvent_RemoteEvent' , true , AllRemoteEvents ) ;
if ( EventName != '' )
{
// Iterate over them, seeing if the name is the one we typed in.
for ( Idx = 0 ; Idx < AllRemoteEvents . length ; Idx ++ )
{
RemoteEvt = SeqEvent _RemoteEvent ( AllRemoteEvents [ Idx ] ) ;
if ( RemoteEvt != None && EventName == RemoteEvt . EventName )
{
bFoundEvt = true ;
// activate the vent
RemoteEvt . CheckActivate ( self , Pawn ) ;
}
}
}
}
` if( ` _ _TW _ )
` if( ` notdefined ( ShippingPC ) )
if ( ! bFoundEvt && ! class 'WorldInfo' . Static . IsConsoleBuild ( ) && ! class 'WorldInfo' . Static . IsConsoleDedicatedServer ( ) )
{
` log("Remote events:");
ClientMessage ( "Remote events:" , , 15.0 ) ;
for ( Idx = 0 ; Idx < AllRemoteEvents . Length ; Idx ++ )
{
RemoteEvt = SeqEvent _RemoteEvent ( AllRemoteEvents [ Idx ] ) ;
if ( RemoteEvt != None && RemoteEvt . bEnabled )
{
` log("-" @ RemoteEvt.EventName);
ClientMessage ( "-" @ RemoteEvt . EventName , , 15.0 ) ;
}
}
}
` endif
` else
if ( ! bFoundEvt )
{
` log("Remote events:");
ClientMessage ( "Remote events:" , , 15.0 ) ;
for ( Idx = 0 ; Idx < AllRemoteEvents . Length ; Idx ++ )
{
RemoteEvt = SeqEvent _RemoteEvent ( AllRemoteEvents [ Idx ] ) ;
if ( RemoteEvt != None && RemoteEvt . bEnabled )
{
` log("-" @ RemoteEvt.EventName);
ClientMessage ( "-" @ RemoteEvt . EventName , , 15.0 ) ;
}
}
}
` endif
}
exec function ShowPlayerState ( )
{
` log("Dumping state stack for" @ Self);
DumpStateStack ( ) ;
}
exec function ShowGameState ( )
{
if ( WorldInfo . Game != None )
{
` log( ` location$ ": Dumping state stack for" @ WorldInfo . Game ) ;
WorldInfo . Game . DumpStateStack ( ) ;
}
else
{
` log( ` location$ ": No GameInfo found!" ) ;
}
}
/ * *
* Notification from pawn that it has received damage
* via TakeDamage ( ) .
* /
function NotifyTakeHit ( Controller InstigatedBy , vector HitLocation , int Damage ,
class < DamageType > damageType , vector Momentum )
{
Super . NotifyTakeHit ( InstigatedBy , HitLocation , Damage , damageType , Momentum ) ;
// Play waveform
ClientPlayForceFeedbackWaveform ( damageType . default . DamagedFFWaveform ) ;
}
/ * *
* Kismet interface for playing / stopping force feedback .
* /
function OnForceFeedback ( SeqAct _ForceFeedback Action )
{
if ( Action . InputLinks [ 0 ] . bHasImpulse )
{
ClientPlayForceFeedbackWaveform ( Action . FFWaveform ) ;
}
else if ( Action . InputLinks [ 1 ] . bHasImpulse )
{
ClientStopForceFeedbackWaveform ( Action . FFWaveform ) ;
}
}
/** This will take an AnimNotify_Rumble and then grab out the correct waveform to be played **/
event PlayRumble ( const AnimNotify _Rumble TheAnimNotify )
{
if ( TheAnimNotify . PredefinedWaveForm != none )
{
ClientPlayForceFeedbackWaveform ( TheAnimNotify . PredefinedWaveForm . default . TheWaveForm ) ;
}
else
{
ClientPlayForceFeedbackWaveform ( TheAnimNotify . WaveForm ) ;
}
}
/ * *
* Tells the client to play a waveform for the specified damage type
*
* @ param FFWaveform The forcefeedback waveform to play
* @ param FFWaveformInstigator the actor causing the waveform to play
* /
reliable client event ClientPlayForceFeedbackWaveform ( ForceFeedbackWaveform FFWaveform , optional Actor FFWaveformInstigator )
{
if ( PlayerInput != None && ! PlayerInput . bUsingGamepad && ! WorldInfo . IsConsoleBuild ( CONSOLE _Any ) )
{
return ; // don't play forcefeedback if gamepad isn't being used
}
if ( ForceFeedbackManager != None && PlayerReplicationInfo != None && IsForceFeedbackAllowed ( ) )
{
ForceFeedbackManager . PlayForceFeedbackWaveform ( FFWaveform , FFWaveformInstigator ) ;
}
}
/ * *
* Tells the client to stop any waveform that is playing . Note if the optional
* parameter is passed in , then the waveform is only stopped if it matches
*
* @ param FFWaveform The forcefeedback waveform to stop
* /
reliable client final event ClientStopForceFeedbackWaveform ( optional ForceFeedbackWaveform FFWaveform )
{
if ( ForceFeedbackManager != None )
{
ForceFeedbackManager . StopForceFeedbackWaveform ( FFWaveform ) ;
}
}
/ * *
* @ return TRUE if starting a force feedback waveform is allowed ; child classes should override this method to e . g . temporarily disable
* force feedback
* /
simulated function bool IsForceFeedbackAllowed ( )
{
return ForceFeedbackManager != None && ForceFeedbackManager . bAllowsForceFeedback ;
}
/ * *
* Handles switching the player in / o u t o f c i n e m a t i c m o d e .
* /
function OnToggleCinematicMode ( SeqAct _ToggleCinematicMode Action )
{
local bool bNewCinematicMode ;
if ( Role < ROLE _Authority )
{
` Warn("Not supported on client");
return ;
}
if ( Action . InputLinks [ 0 ] . bHasImpulse )
{
bNewCinematicMode = TRUE ;
}
else if ( Action . InputLinks [ 1 ] . bHasImpulse )
{
bNewCinematicMode = FALSE ;
}
else if ( Action . InputLinks [ 2 ] . bHasImpulse )
{
bNewCinematicMode = ! bCinematicMode ;
}
SetCinematicMode ( bNewCinematicMode , Action . bHidePlayer , Action . bHideHUD , Action . bDisableMovement , Action . bDisableTurning , Action . bDisableInput , Action . bAllowDofChanges ) ;
}
/ * *
* Server / SP only function for changing whether the player is in cinematic mode . Updates values of various state variables , then replicates the call to the client
* to sync the current cinematic mode .
*
* @ param bInCinematicMode specify TRUE if the player is entering cinematic mode ; FALSE if the player is leaving cinematic mode .
* @ param bHidePlayer specify TRUE to hide the player ' s pawn ( only relevant if bInCinematicMode is TRUE )
* @ param bAffectsHUD specify TRUE if we should show / hide the HUD to match the value of bCinematicMode
* @ param bAffectsMovement specify TRUE to disable movement in cinematic mode , enable it when leaving
* @ param bAffectsTurning specify TRUE to disable turning in cinematic mode or enable it when leaving
* @ param bAffectsButtons specify TRUE to disable button input in cinematic mode or enable it when leaving .
* @ param bAffectsDof specify TRUE to apply depth of field changes in cinematic mode // @TWI_BEGIN
* /
function SetCinematicMode ( bool bInCinematicMode , bool bHidePlayer , bool bAffectsHUD , bool bAffectsMovement , bool bAffectsTurning , bool bAffectsButtons , optional bool bAffectsDof = true )
{
local bool bAdjustMoveInput , bAdjustLookInput ;
bCinematicMode = bInCinematicMode ;
// if now in cinematic mode
if ( bCinematicMode )
{
// hide the player
if ( Pawn != None && bHidePlayer )
{
Pawn . SetHidden ( True ) ;
}
}
else
{
if ( Pawn != None )
{
Pawn . SetHidden ( False ) ;
}
}
bAdjustMoveInput = bAffectsMovement && ( bCinematicMode != bCinemaDisableInputMove ) ;
bAdjustLookInput = bAffectsTurning && ( bCinematicMode != bCinemaDisableInputLook ) ;
if ( bAdjustMoveInput )
{
IgnoreMoveInput ( bCinematicMode ) ;
bCinemaDisableInputMove = bCinematicMode ;
}
if ( bAdjustLookInput )
{
IgnoreLookInput ( bCinematicMode ) ;
bCinemaDisableInputLook = bCinematicMode ;
}
ClientSetCinematicMode ( bCinematicMode , bAdjustMoveInput , bAdjustLookInput , bAffectsHUD , bAffectsDof ) ;
}
/** called by the server to synchronize cinematic transitions with the client */
reliable client function ClientSetCinematicMode ( bool bInCinematicMode , bool bAffectsMovement , bool bAffectsTurning , bool bAffectsHUD , bool bAffectsDof )
{
bCinematicMode = bInCinematicMode ;
// if there's a hud, set whether it should be shown or not
if ( ( myHUD != None ) && bAffectsHUD )
{
myHUD . bShowHUD = ! bCinematicMode ;
}
if ( bAffectsMovement )
{
IgnoreMoveInput ( bCinematicMode ) ;
}
if ( bAffectsTurning )
{
IgnoreLookInput ( bCinematicMode ) ;
}
}
/** Toggles move input. FALSE means movement input is cleared. */
function IgnoreMoveInput ( bool bNewMoveInput )
{
bIgnoreMoveInput = Max ( bIgnoreMoveInput + ( bNewMoveInput ? + 1 : - 1 ) , 0 ) ;
//`Log("IgnoreMove: " $ bIgnoreMoveInput);
}
/** return TRUE if movement input is ignored. */
event bool IsMoveInputIgnored ( )
{
return ( bIgnoreMoveInput > 0 ) ;
}
/** Toggles look input. FALSE means look input is cleared. */
function IgnoreLookInput ( bool bNewLookInput )
{
bIgnoreLookInput = Max ( bIgnoreLookInput + ( bNewLookInput ? + 1 : - 1 ) , 0 ) ;
//`Log("IgnoreLook: " $ bIgnoreLookInput);
}
/** return TRUE if look input is ignored. */
event bool IsLookInputIgnored ( )
{
return ( bIgnoreLookInput > 0 ) ;
}
/** reset input to defaults */
function ResetPlayerMovementInput ( )
{
bIgnoreMoveInput = default . bIgnoreMoveInput ;
bIgnoreLookInput = default . bIgnoreLookInput ;
}
/** Kismet hook to trigger console events */
function OnConsoleCommand ( SeqAct _ConsoleCommand inAction )
{
local string Command ;
foreach inAction . Commands ( Command )
{
// prevent "set" commands from ever working in Kismet as they are e.g. disabled in netplay
if ( ! ( Left ( Command , 4 ) ~ = "set " ) && ! ( Left ( Command , 9 ) ~ = "setnopec " ) )
{
ConsoleCommand ( Command ) ;
}
}
}
/** forces GC at the end of the tick on the client */
reliable client event ClientForceGarbageCollection ( )
{
WorldInfo . ForceGarbageCollection ( ) ;
}
final event LevelStreamingStatusChanged ( LevelStreaming LevelObject , bool bNewShouldBeLoaded , bool bNewShouldBeVisible , bool bNewShouldBlockOnLoad )
{
//`log( "LevelStreamingStatusChanged: " @ LevelObject @ bNewShouldBeLoaded @ bNewShouldBeVisible @ bNewShouldBeVisible );
ClientUpdateLevelStreamingStatus ( LevelObject . PackageName , bNewShouldBeLoaded , bNewShouldBeVisible , bNewShouldBlockOnLoad ) ;
}
native reliable client function ClientUpdateLevelStreamingStatus ( Name PackageName , bool bNewShouldBeLoaded , bool bNewShouldBeVisible , bool bNewShouldBlockOnLoad ) ;
/ * * c a l l e d w h e n t h e c l i e n t a d d s / r e m o v e s a s t r e a m e d l e v e l
* the server will only replicate references to Actors in visible levels so that it ' s impossible to send references to
* Actors the client has not initialized
* @ param PackageName the name of the package for the level whose status changed
* /
native reliable server final event ServerUpdateLevelVisibility ( name PackageName , bool bIsVisible ) ;
/ * * a s y n c h r o n o u s l y l o a d s t h e g i v e n l e v e l i n p r e p a r a t i o n f o r a s t r e a m i n g m a p t r a n s i t i o n .
* the server sends one function per level name since dynamic arrays can ' t be replicated
* @ param LevelNames the names of the level packages to load . LevelNames [ 0 ] will be the new persistent ( primary ) level
* @ param bFirst whether this is the first item in the list ( so clear the list first )
* @ param bLast whether this is the last item in the list ( so start preparing the change after receiving it )
* /
reliable client event ClientPrepareMapChange ( name LevelName , bool bFirst , bool bLast )
{
// Only call on the first local player controller to handle it being called on multiple PCs for splitscreen.
local PlayerController PC ;
foreach LocalPlayerControllers ( class 'PlayerController' , PC )
{
if ( PC != self )
{
return ;
}
else
{
break ;
}
}
if ( bFirst )
{
PendingMapChangeLevelNames . length = 0 ;
ClearTimer ( 'DelayedPrepareMapChange' ) ;
}
PendingMapChangeLevelNames [ PendingMapChangeLevelNames . length ] = LevelName ;
if ( bLast )
{
DelayedPrepareMapChange ( ) ;
}
}
/** used to wait until a map change can be prepared when one was already in progress */
function DelayedPrepareMapChange ( )
{
if ( WorldInfo . IsPreparingMapChange ( ) )
{
// we must wait for the previous one to complete
SetTimer ( 0.01 , false , nameof ( DelayedPrepareMapChange ) ) ;
}
else
{
WorldInfo . PrepareMapChange ( PendingMapChangeLevelNames ) ;
}
}
/** actually performs the level transition prepared by PrepareMapChange() */
reliable client event ClientCommitMapChange ( )
{
if ( IsTimerActive ( nameof ( DelayedPrepareMapChange ) ) )
{
SetTimer ( 0.01 , false , nameof ( ClientCommitMapChange ) ) ;
}
else
{
if ( Pawn != None )
{
SetViewTarget ( Pawn ) ;
}
else
{
SetViewTarget ( self ) ;
}
WorldInfo . CommitMapChange ( ) ;
}
}
/** tells client to cancel any pending map change */
reliable client event ClientCancelPendingMapChange ( )
{
WorldInfo . CancelPendingMapChange ( ) ;
}
/ * * t e l l s t h e c l i e n t t o b l o c k u n t i l a l l p e n d i n g l e v e l s t r e a m i n g a c t i o n s a r e c o m p l e t e
* happens at the end of the tick
* primarily used to force update the client ASAP at join time
* /
reliable client native final event ClientFlushLevelStreaming ( ) ;
/ * * s e t s b R e q u e s t e d B l o c k O n A s y n c L o a d i n g w h i c h w i l l l a t e r b r i n g u p a l o a d i n g s c r e e n a n d t h e n f i n i s h a n y a s y n c l o a d i n g i n p r o g r e s s
* called automatically on all clients whenever something triggers it on the server
* /
reliable client event ClientSetBlockOnAsyncLoading ( )
{
WorldInfo . bRequestedBlockOnAsyncLoading = true ;
}
/** used to allow clients to process a SeqAct_WaitForLevelsVisible by blocking, if required */
reliable client function ClientWaitForLevelsVisible ( SeqAct _WaitForLevelsVisible InAction )
{
InAction . CheckLevelsVisible ( ) ;
}
/ * *
* Force a save config on the specified class .
* /
exec function SaveClassConfig ( coerce string className )
{
local class < Object > saveClass ;
` log("SaveClassConfig:"@className);
saveClass = class < Object > ( DynamicLoadObject ( className , class 'class' ) ) ;
if ( saveClass != None )
{
` log("- Saving config on:"@saveClass);
saveClass . static . StaticSaveConfig ( ) ;
}
` if( ` notdefined ( FINAL _RELEASE ) )
else
{
` log("- Failed to find class:"@className);
}
` endif
}
/ * *
* Force a save config on the specified actor .
* /
exec function SaveActorConfig ( coerce Name actorName )
{
local Actor chkActor ;
` log("SaveActorConfig:"@actorName);
foreach AllActors ( class 'Actor' , chkActor )
{
if ( chkActor != None &&
chkActor . Name == actorName )
{
` log("- Saving config on:"@chkActor);
chkActor . SaveConfig ( ) ;
}
}
}
/ * *
* Returns the interaction that manages the UI system .
* /
final function UIInteraction GetUIController ( )
{
local LocalPlayer LP ;
local UIInteraction Result ;
LP = LocalPlayer ( Player ) ;
if ( LP != None && LP . ViewportClient != None )
{
Result = LP . ViewportClient . UIController ;
}
return Result ;
}
/ * *
* Native function to determine if voice data should be received from this player .
* Only called on the server to determine whether voice packet replication
* should happen for the given sender .
*
* NOTE : This function is final because it can be called n ^ 2 number of times
* in a given frame , where n is the number of players . Change / overload this
* function with caution as this can affect your network performance .
*
* @ param Sender the player to check for mute status
*
* @ return TRUE if this player is muted , FALSE otherwise
* /
native final function bool IsPlayerMuted ( const out UniqueNetId Sender ) ;
2024-01-23 16:25:12 +00:00
native final function bool IsClientMinimized ( ) ;
native final function bool IsClientInForeground ( ) ;
2020-12-13 15:01:13 +00:00
` if( ` _ _TW _ )
native final function bool ShouldReplicateVoicePacketFrom ( const out UniqueNetId Sender ) ;
native final function bool ShouldReplicateVoicePacketTo ( const out UniqueNetId Receiver ) ;
` endif
` if( ` _ _TW _ )
function ForceDisconnect ( ) ;
` endif
/ * * c a l l e d o n c l i e n t d u r i n g s e a m l e s s l e v e l t r a n s i t i o n s t o g e t t h e l i s t o f A c t o r s t h a t s h o u l d b e m o v e d i n t o t h e n e w l e v e l
* PlayerControllers , Role < ROLE _Authority Actors , and any non - Actors that are inside an Actor that is in the list
* ( i . e . Object . Outer == Actor in the list )
* are all autmoatically moved regardless of whether they ' re included here
* only dynamic ( ! bStatic and ! bNoDelete ) actors in the PersistentLevel may be moved ( this includes all actors spawned during gameplay )
* this is called for both parts of the transition because actors might change while in the middle ( e . g . players might join or leave the game )
* @ see also GameInfo : : GetSeamlessTravelActorList ( ) ( the function that ' s called on servers )
* @ param bToEntry true if we are going from old level - > entry , false if we are going from entry - > new level
* @ param ActorList ( out ) list of actors to maintain
* /
event GetSeamlessTravelActorList ( bool bToEntry , out array < Actor > ActorList )
{
// clear out audio pool
HearSoundActiveComponents . length = 0 ;
HearSoundPoolComponents . length = 0 ;
if ( myHUD != None )
{
ActorList [ ActorList . length ] = myHUD ;
}
}
/ * * c a l l e d w h e n s e a m l e s s t r a v e l l i n g a n d w e a r e b e i n g r e p l a c e d b y t h e s p e c i f i e d P C
* clean up any persistent state ( post process chains on LocalPlayers , for example )
* ( not called if PlayerControllerClass is the same for the from and to gametypes )
* /
function SeamlessTravelTo ( PlayerController NewPC ) ;
/ * * c a l l e d w h e n s e a m l e s s t r a v e l l i n g a n d t h e s p e c i f i e d P C i s b e i n g r e p l a c e d b y t h i s o n e
* copy over data that should persist
* ( not called if PlayerControllerClass is the same for the from and to gametypes )
* /
function SeamlessTravelFrom ( PlayerController OldPC )
{
// copy PRI data
OldPC . PlayerReplicationInfo . Reset ( ) ;
OldPC . PlayerReplicationInfo . SeamlessTravelTo ( PlayerReplicationInfo ) ;
// mark the old PC as not a player, so it doesn't get Logout() etc when they player represented didn't really leave
OldPC . bIsPlayer = false ;
//@fixme: need a way to replace PRIs that doesn't cause incorrect "player left the game"/"player entered the game" messages
OldPC . PlayerReplicationInfo . Destroy ( ) ;
OldPC . PlayerReplicationInfo = None ;
}
/ * *
* Looks at the current game state and uses that to set the
* rich presence strings
*
* Licensees should override this in their player controller derived class
* /
reliable client function ClientSetOnlineStatus ( ) ;
/ * *
* Returns the player controller associated with this net id
*
* @ param PlayerNetId the id to search for
*
* @ return the player controller if found , otherwise none
* /
native static function PlayerController GetPlayerControllerFromNetId ( UniqueNetId PlayerNetId ) ;
/ * *
* Tells the client that the server has all the information it needs and that it
* is ok to start sending voice packets . The server will already send voice packets
* when this function is called , since it is set server side and then forwarded
*
* NOTE : This is done as an RPC instead of variable replication because ordering matters
* /
reliable client function ClientVoiceHandshakeComplete ( )
{
local int PeerIdx ;
bHasVoiceHandshakeCompleted = true ;
// Replicate list of connected peers for each player to the server
for ( PeerIdx = 0 ; PeerIdx < ConnectedPeers . Length ; PeerIdx ++ )
{
ServerAddPeer ( ConnectedPeers [ PeerIdx ] . PlayerId , ConnectedPeers [ PeerIdx ] . NATType ) ;
}
}
/ * *
* Locally mutes a remote player
*
* @ param PlayerNetId the remote player to mute
* /
reliable client event ClientMutePlayer ( UniqueNetId PlayerNetId )
{
local LocalPlayer LocPlayer ;
// Add to the filter list on clients (used for peer to peer voice)
if ( VoicePacketFilter . Find ( 'Uid' , PlayerNetId . Uid ) == INDEX _NONE )
{
VoicePacketFilter . AddItem ( PlayerNetId ) ;
}
if ( VoiceInterface != None )
{
// Use the local player to determine the controller id
LocPlayer = LocalPlayer ( Player ) ;
if ( LocPlayer != None )
{
// Have the voice subsystem mute this player
VoiceInterface . MuteRemoteTalker ( LocPlayer . ControllerId , PlayerNetId ) ;
}
}
}
/ * *
* Locally unmutes a remote player
*
* @ param PlayerNetId the remote player to unmute
* /
reliable client event ClientUnmutePlayer ( UniqueNetId PlayerNetId )
{
local LocalPlayer LocPlayer ;
local int RemoveIndex ;
// It's safe to remove them from the filter list on clients (used for peer to peer voice)
RemoveIndex = VoicePacketFilter . Find ( 'Uid' , PlayerNetId . Uid ) ;
if ( RemoveIndex != INDEX _NONE )
{
VoicePacketFilter . Remove ( RemoveIndex , 1 ) ;
}
if ( VoiceInterface != None )
{
// Use the local player to determine the controller id
LocPlayer = LocalPlayer ( Player ) ;
if ( LocPlayer != None )
{
// Have the voice subsystem restore voice for this player
VoiceInterface . UnmuteRemoteTalker ( LocPlayer . ControllerId , PlayerNetId ) ;
}
}
}
/ * *
* Mutes a remote player on the server and then tells the client to mute
*
* @ param PlayerNetId the remote player to mute
* /
function GameplayMutePlayer ( UniqueNetId PlayerNetId )
{
// Don't add if already muted
if ( GameplayVoiceMuteList . Find ( 'Uid' , PlayerNetId . Uid ) == INDEX _NONE )
{
GameplayVoiceMuteList . AddItem ( PlayerNetId ) ;
}
// Add to the filter list too, if missing
if ( VoicePacketFilter . Find ( 'Uid' , PlayerNetId . Uid ) == INDEX _NONE )
{
VoicePacketFilter . AddItem ( PlayerNetId ) ;
}
// Now process on the client needed for splitscreen net play
ClientMutePlayer ( PlayerNetId ) ;
}
/ * *
* Unmutes a remote player on the server and then tells the client to unmute
*
* @ param PlayerNetId the remote player to unmute
* /
function GameplayUnmutePlayer ( UniqueNetId PlayerNetId )
{
local int RemoveIndex ;
local PlayerController Other ;
// Remove from the gameplay mute list
RemoveIndex = GameplayVoiceMuteList . Find ( 'Uid' , PlayerNetId . Uid ) ;
if ( RemoveIndex != INDEX _NONE )
{
GameplayVoiceMuteList . Remove ( RemoveIndex , 1 ) ;
}
// Find the muted player's player controller so it can be notified
Other = GetPlayerControllerFromNetId ( PlayerNetId ) ;
if ( Other != None )
{
// If they were not explicitly muted
if ( VoiceMuteList . Find ( 'Uid' , PlayerNetId . Uid ) == INDEX _NONE &&
// and they did not explicitly mute us
Other . VoiceMuteList . Find ( 'Uid' , PlayerReplicationInfo . UniqueId . Uid ) == INDEX _NONE )
{
// It's safe to remove them from the filter list
RemoveIndex = VoicePacketFilter . Find ( 'Uid' , PlayerNetId . Uid ) ;
if ( RemoveIndex != INDEX _NONE )
{
VoicePacketFilter . Remove ( RemoveIndex , 1 ) ;
}
// Now process on the client
ClientUnmutePlayer ( PlayerNetId ) ;
}
}
}
/ * *
* Updates the server side information by adding to the mute list . Tells the
* player controller that owns the specified net id to also mute this PC .
*
* @ param PlayerNetId the remote player to mute
* /
//@HSL_BEGIN - JRO - 8/2/2016 - Adding the option of 1-way muting.
reliable server event ServerMutePlayer ( UniqueNetId PlayerNetId , optional bool bMuteOther = true )
//@HSL_END
{
local PlayerController Other ;
// Don't reprocess if they are already muted
if ( VoiceMuteList . Find ( 'Uid' , PlayerNetId . Uid ) == INDEX _NONE )
{
VoiceMuteList . AddItem ( PlayerNetId ) ;
}
// Add them to the packet filter list if not already on it
if ( VoicePacketFilter . Find ( 'Uid' , PlayerNetId . Uid ) == INDEX _NONE )
{
VoicePacketFilter . AddItem ( PlayerNetId ) ;
}
ClientMutePlayer ( PlayerNetId ) ;
// Find the muted player's player controller so it can be notified
Other = GetPlayerControllerFromNetId ( PlayerNetId ) ;
if ( Other != None && bMuteOther ) //@HSL - JRO - 8/2/2016 - 1-way muting
{
// Update their packet filter list too
if ( Other . VoicePacketFilter . Find ( 'Uid' , PlayerReplicationInfo . UniqueId . Uid ) == INDEX _NONE )
{
Other . VoicePacketFilter . AddItem ( PlayerReplicationInfo . UniqueId ) ;
}
// Tell the other PC to mute this one
Other . ClientMutePlayer ( PlayerReplicationInfo . UniqueId ) ;
}
}
/ * *
* Updates the server side information by removing from the mute list . Tells the
* player controller that owns the specified net id to also unmute this PC .
*
* @ param PlayerNetId the remote player to unmute
* /
//@HSL_BEGIN - JRO - 8/2/2016 - Adding the option of 1-way muting.
reliable server event ServerUnmutePlayer ( UniqueNetId PlayerNetId , optional bool bUnmuteOther = true )
//@HSL_END
{
local PlayerController Other ;
local int RemoveIndex ;
RemoveIndex = VoiceMuteList . Find ( 'Uid' , PlayerNetId . Uid ) ;
// If the player was found, remove them from our explicit list
if ( RemoveIndex != INDEX _NONE )
{
VoiceMuteList . Remove ( RemoveIndex , 1 ) ;
}
//@HSL_BEGIN - JRO - 8/2/2016 - Adding the option of 1-way muting.
if ( ! bUnmuteOther )
{
// Add them to the packet filter list if not already on it
RemoveIndex = VoicePacketFilter . Find ( 'Uid' , PlayerNetId . Uid ) ;
if ( RemoveIndex != INDEX _NONE )
{
VoicePacketFilter . Remove ( RemoveIndex , 1 ) ;
}
ClientUnmutePlayer ( PlayerNetId ) ;
}
//@HSL_END
// Find the muted player's player controller so it can be notified
Other = GetPlayerControllerFromNetId ( PlayerNetId ) ;
if ( Other != None && bUnmuteOther ) //@HSL - JRO - 8/2/2016 - 1-way muting
{
// Make sure this player isn't muted for gameplay reasons
if ( GameplayVoiceMuteList . Find ( 'Uid' , PlayerNetId . Uid ) == INDEX _NONE &&
// And make sure they didn't mute us
Other . VoiceMuteList . Find ( 'Uid' , PlayerReplicationInfo . UniqueId . Uid ) == INDEX _NONE )
{
ClientUnmutePlayer ( PlayerNetId ) ;
}
// If the other player doesn't have this player muted
if ( Other . VoiceMuteList . Find ( 'Uid' , PlayerReplicationInfo . UniqueId . Uid ) == INDEX _NONE &&
Other . GameplayVoiceMuteList . Find ( 'Uid' , PlayerReplicationInfo . UniqueId . Uid ) == INDEX _NONE )
{
// Remove them from the packet filter list
RemoveIndex = VoicePacketFilter . Find ( 'Uid' , PlayerNetId . Uid ) ;
if ( RemoveIndex != INDEX _NONE )
{
VoicePacketFilter . Remove ( RemoveIndex , 1 ) ;
}
// If found, remove so packets flow to that client too
RemoveIndex = Other . VoicePacketFilter . Find ( 'Uid' , PlayerReplicationInfo . UniqueId . Uid ) ;
if ( RemoveIndex != INDEX _NONE )
{
Other . VoicePacketFilter . Remove ( RemoveIndex , 1 ) ;
}
// Tell the other PC to unmute this one
Other . ClientUnmutePlayer ( PlayerReplicationInfo . UniqueId ) ;
}
}
}
/** notification when a matinee director track starts or stops controlling the ViewTarget of this PlayerController */
event NotifyDirectorControl ( bool bNowControlling , SeqAct _Interp CurrentMatinee )
{
// matinee is done, make sure client syncs up viewtargets, since we were ignoring
// ClientSetViewTarget during the matinee.
if ( ! bNowControlling && ( WorldInfo . NetMode == NM _Client ) && bClientSimulatingViewTarget )
{
ServerVerifyViewTarget ( ) ;
}
}
/ * *
* This will turn the subtitles on or off depending on the value of bValue
*
* @ param bValue to show or not to show
* * /
native simulated exec function SetShowSubtitles ( bool bValue ) ;
/ * *
* This will turn return whether the subtitles are on or off
*
* * /
native simulated function bool IsShowingSubtitles ( ) ;
reliable client event ClientWasKicked ( ) ;
/ * *
* Tells the client to register with arbitration . The client must notify the
* server once that is complete or it will be kicked from the match
* /
reliable client function ClientRegisterForArbitration ( )
{
if ( OnlineSub != None && OnlineSub . GameInterface != None )
{
OnlineSub . GameInterface . AddArbitrationRegistrationCompleteDelegate ( OnArbitrationRegisterComplete ) ;
// Kick off async arbitration registration
OnlineSub . GameInterface . RegisterForArbitration ( 'Game' ) ;
}
else
{
// Fake completion with the server for flow testing
ServerRegisteredForArbitration ( true ) ;
}
}
/ * *
* Delegate that is notified when registration is complete . Forwards the call
* to the server so that it can finalize processing
*
* @ param SessionName the name of the session this is for
* @ param bWasSuccessful whether registration worked or not
* /
function OnArbitrationRegisterComplete ( name SessionName , bool bWasSuccessful )
{
// Clear the delegate since it isn't needed
OnlineSub . GameInterface . ClearArbitrationRegistrationCompleteDelegate ( OnArbitrationRegisterComplete ) ;
// Tell the server that we completed registration
ServerRegisteredForArbitration ( bWasSuccessful ) ;
}
/ * *
* Notifies the server that the arbitration registration is complete
*
* @ param bWasSuccessful whether the registration with arbitration worked
* /
reliable server function ServerRegisteredForArbitration ( bool bWasSuccessful )
{
// Tell the game info this PC is done and whether it worked or not
WorldInfo . Game . ProcessClientRegistrationCompletion ( self , bWasSuccessful ) ;
}
/ * *
* Delegate called when the user accepts a game invite externally . This allows
* the game code a chance to clean up before joining the game via
* AcceptGameInvite ( ) call .
*
* NOTE : There must be space for all signed in players to join the game . All
* players must also have permission to play online too .
*
* @ param InviteResult the search / settings for the game we ' re to join
* /
//@HSL_BEGIN - JRO - 3/21/2016 - PS4 Sessions
function OnGameInviteAccepted ( const out OnlineGameSearchResult InviteResult , OnGameInviteAcceptedResult ResultReason )
//@HSL_END
{
local OnlineGameSettings GameInviteSettings ;
` log("SESSIONS - OnGameInviteAccepted");
if ( OnlineSub != None && OnlineSub . GameInterface != None )
{
GameInviteSettings = InviteResult . GameSettings ;
if ( GameInviteSettings != None )
{
// Make sure the new game has space
if ( InviteHasEnoughSpace ( GameInviteSettings ) )
{
// Make sure everyone logged in can play online
if ( CanAllPlayersPlayOnline ( ) )
{
//@HSL_BEGIN - JRO - 3/21/2016 - Make sure we still have the new session info after we leave the current one
CachedInviteResult = InviteResult ;
//@HSL_END
if ( WorldInfo . NetMode != NM _Standalone )
{
// Write arbitration data, if required
if ( OnlineSub . GameInterface . GetGameSettings ( 'Game' ) . bUsesArbitration )
{
// Write out our version of the scoring before leaving
ClientWriteOnlinePlayerScores ( WorldInfo . GRI . GameClass != None ? WorldInfo . GRI . GameClass . default . ArbitratedLeaderBoardId : 0 ) ;
}
// Set the end delegate, where we'll destroy the game and then join
OnlineSub . GameInterface . AddEndOnlineGameCompleteDelegate ( OnEndForInviteComplete ) ;
// Force the flush of the stats
OnlineSub . GameInterface . EndOnlineGame ( 'Game' ) ;
}
else
{
// Set the delegate for notification of the join completing
OnlineSub . GameInterface . AddJoinOnlineGameCompleteDelegate ( OnInviteJoinComplete ) ;
// We can immediately accept since there is no online game
//@HSL_BEGIN - JRO - 3/21/2016 - Adding invite info
if ( ! OnlineSub . GameInterface . AcceptGameInvite ( LocalPlayer ( Player ) . ControllerId , 'Game' , CachedInviteResult ) )
//@HSL_END
{
OnlineSub . GameInterface . ClearJoinOnlineGameCompleteDelegate ( OnInviteJoinComplete ) ;
// Do some error handling
NotifyInviteFailed ( ) ;
}
}
}
else
{
// Display an error message
NotifyNotAllPlayersCanJoinInvite ( ) ;
}
}
else
{
// Display an error message
NotifyNotEnoughSpaceInInvite ( ) ;
}
}
else
{
// Display an error message
NotifyInviteFailed ( ) ;
}
}
}
//@HSL_BEGIN - JRO - 6/10/2016 - Overloaded in KFPlayerController
function OnConnectionStatusChange ( EOnlineServerConnectionStatus ConnectionStatus )
{
}
function OnPlayTogetherStarted ( )
{
}
function JoinPlayfabServer ( bool bWasSuccessful , string ServerIP )
{
}
//@HSL_END
/ * *
* Counts the number of local players to verify there is enough space
*
* @ return true if there is sufficient space , false if not
* /
function bool InviteHasEnoughSpace ( OnlineGameSettings InviteSettings )
{
local int NumLocalPlayers ;
local PlayerController PC ;
foreach LocalPlayerControllers ( class 'PlayerController' , PC )
{
NumLocalPlayers ++ ;
}
// Invites consume private connections
return ( InviteSettings . NumOpenPrivateConnections + InviteSettings . NumOpenPublicConnections ) >= NumLocalPlayers ;
}
/ * *
* Validates that each local player can play online
*
* @ return true if there is sufficient space , false if not
* /
function bool CanAllPlayersPlayOnline ( )
{
local PlayerController PC ;
local LocalPlayer LocPlayer ;
foreach LocalPlayerControllers ( class 'PlayerController' , PC )
{
LocPlayer = LocalPlayer ( PC . Player ) ;
if ( LocPlayer != None )
{
// Check their login status and permissions
if ( OnlineSub . PlayerInterface . GetLoginStatus ( LocPlayer . ControllerId ) != LS _LoggedIn ||
//@HSL_BEGIN_XBOX
! PC . bCanPlayOnline )
//@HSL_END_XBOX
{
return false ;
}
}
else
{
return false ;
}
}
return true ;
}
/ * *
* Clears all of the invite delegates
* /
function ClearInviteDelegates ( )
{
// Clear the end delegate
OnlineSub . GameInterface . ClearEndOnlineGameCompleteDelegate ( OnEndForInviteComplete ) ;
// Clear the destroy delegate
OnlineSub . GameInterface . ClearDestroyOnlineGameCompleteDelegate ( OnDestroyForInviteComplete ) ;
// Set the delegate for notification of the join completing
OnlineSub . GameInterface . ClearJoinOnlineGameCompleteDelegate ( OnInviteJoinComplete ) ;
}
/ * *
* Delegate called once the destroy of an online game before accepting an invite
* is complete . From here , the game invite can be accepted
*
* @ param SessionName the name of the session being ended
* @ param bWasSuccessful whether the end completed ok or not
* /
function OnEndForInviteComplete ( name SessionName , bool bWasSuccessful )
{
// Set the destroy delegate so we can know when that is complete
OnlineSub . GameInterface . AddDestroyOnlineGameCompleteDelegate ( OnDestroyForInviteComplete ) ;
// Now we can destroy the game (completion delegate guaranteed to be called)
OnlineSub . GameInterface . DestroyOnlineGame ( SessionName ) ;
}
/ * *
* Delegate called once the destroy of an online game before accepting an invite
* is complete . From here , the game invite can be accepted
*
* @ param SessionName the name of the session being ended
* @ param bWasSuccessful whether the end completed ok or not
* /
function OnDestroyForInviteComplete ( name SessionName , bool bWasSuccessful )
{
` log("SESSIONS - Destroy for invite complete for session name"@SessionName@"and successful"@bWasSuccessful);
if ( bWasSuccessful )
{
// Set the delegate for notification of the join completing
OnlineSub . GameInterface . AddJoinOnlineGameCompleteDelegate ( OnInviteJoinComplete ) ;
// This will have us join async
if ( ! OnlineSub . GameInterface . AcceptGameInvite ( LocalPlayer ( Player ) . ControllerId , SessionName , CachedInviteResult ) ) //@HSL - JRO - 3/21/2016 - PS4 Sessions - Make sure we accept the correct game!
{
OnlineSub . GameInterface . ClearJoinOnlineGameCompleteDelegate ( OnInviteJoinComplete ) ;
// Do some error handling
NotifyInviteFailed ( ) ;
}
}
else
{
// Do some error handling
NotifyInviteFailed ( ) ;
}
}
/ * *
* Allows the game to modify the URL that clients will use to connect to a server ( should be called
* by pretty much anyone who calls GetResolvedConnectString ( ) )
* /
function string ModifyClientURL ( string URL )
{
return URL ;
}
/ * *
* Once the join completes , use the platform specific connection information
* to connect to it
*
* @ param SessionName the name of the session that was joined
* @ param bWasSuccessful whether the join worked or not
* /
function OnInviteJoinComplete ( name SessionName , bool bWasSuccessful )
{
local string URL ;
if ( bWasSuccessful )
{
if ( OnlineSub != None && OnlineSub . GameInterface != None )
{
// Get the platform specific information
if ( OnlineSub . GameInterface . GetResolvedConnectString ( SessionName , URL ) )
{
URL $ = "?bIsFromInvite" ;
// allow game to override
URL = ModifyClientURL ( URL ) ;
` Log("Resulting url is (" $ URL $ ")");
// Open a network connection to it
ClientTravel ( URL , TRAVEL _Absolute ) ;
}
}
}
else
{
// Do some error handling
NotifyInviteFailed ( ) ;
}
ClearInviteDelegates ( ) ;
}
/** Override to display a message to the user */
function NotifyInviteFailed ( optional string LocKey = "UnableToJoinInvite" )
{
` Log("SESSIONS - Invite handling failed");
ClearInviteDelegates ( ) ;
}
/** Override to display a message to the user */
function NotifyNotAllPlayersCanJoinInvite ( )
{
` Log("Not all local players have permission to join the game invite");
}
/** Override to display a message to the user */
function NotifyNotEnoughSpaceInInvite ( )
{
` Log("Not enough space for all local players in the game invite");
}
/ * *
* Called when an arbitrated match has ended and we need to disconnect
* /
reliable client function ClientArbitratedMatchEnded ( )
{
ConsoleCommand ( "Disconnect" ) ;
}
/ * *
* Writes the scores for all active players . Override this in your
* playercontroller class to provide custom scoring
*
* @ param LeaderboardId the leaderboard the scores are being written to
* /
reliable client function ClientWriteOnlinePlayerScores ( int LeaderboardId )
{
local GameReplicationInfo GRI ;
local int Index ;
local array < OnlinePlayerScore > PlayerScores ;
local UniqueNetId ZeroUniqueId ;
local bool bIsTeamGame ;
local int ScoreIndex ;
GRI = WorldInfo . GRI ;
if ( GRI != None && OnlineSub != None && OnlineSub . StatsInterface != None )
{
// Look at the default object of the gameinfo to determine if this is a
// team game or not
bIsTeamGame = GRI . GameClass != None ? GRI . GameClass . default . bTeamGame : false ;
// Iterate through the players building their score data
for ( Index = 0 ; Index < GRI . PRIArray . Length ; Index ++ )
{
if ( GRI . PRIArray [ Index ] . UniqueId != ZeroUniqueId )
{
if ( bIsTeamGame )
{
// Players without teams in team games are excluded from submitted scores
if ( GRI . PRIArray [ Index ] . Team != None )
{
ScoreIndex = PlayerScores . Length ;
PlayerScores . Length = ScoreIndex + 1 ;
// Build the skill data for this player
PlayerScores [ ScoreIndex ] . PlayerId = GRI . PRIArray [ Index ] . UniqueId ;
PlayerScores [ ScoreIndex ] . TeamId = GRI . PRIArray [ Index ] . Team . TeamIndex ;
PlayerScores [ ScoreIndex ] . Score = GRI . PRIArray [ Index ] . Team . Score ;
}
}
else
{
ScoreIndex = PlayerScores . Length ;
PlayerScores . Length = ScoreIndex + 1 ;
// Build the skill data for this player
PlayerScores [ ScoreIndex ] . PlayerId = GRI . PRIArray [ Index ] . UniqueId ;
PlayerScores [ ScoreIndex ] . TeamId = Index ;
PlayerScores [ ScoreIndex ] . Score = GRI . PRIArray [ Index ] . Score ;
}
}
}
// Now write out the scores
OnlineSub . StatsInterface . WriteOnlinePlayerScores ( PlayerReplicationInfo . SessionName , LeaderboardId , PlayerScores ) ;
}
}
/ * *
* Tells the clients to write the stats using the specified stats object
*
* @ param OnlineStatsWriteClass the stats class to write with
* @ param bIsIncomplete TRUE if the match wasn ' t finished at the time of the write
* /
reliable client function ClientWriteLeaderboardStats ( class < OnlineStatsWrite > OnlineStatsWriteClass , optional bool bIsIncomplete = false ) ;
/ * *
* Sets the host ' s net id for handling dropped arbitrated matches
*
* @ param InHostId the host ' s unique net id to report the drop for
* /
reliable client function ClientSetHostUniqueId ( UniqueNetId InHostId ) ;
/** Tells this client that it should not send voice data over the network */
reliable client function ClientStopNetworkedVoice ( )
{
local LocalPlayer LocPlayer ;
LocPlayer = LocalPlayer ( Player ) ;
if ( LocPlayer != None && OnlineSub != None && OnlineSub . VoiceInterface != None )
{
OnlineSub . VoiceInterface . StopNetworkedVoice ( LocPlayer . ControllerId ) ;
}
}
/** Tells this client that it should send voice data over the network */
reliable client function ClientStartNetworkedVoice ( )
{
local LocalPlayer LocPlayer ;
LocPlayer = LocalPlayer ( Player ) ;
if ( LocPlayer != None && OnlineSub != None && OnlineSub . VoiceInterface != None )
{
OnlineSub . VoiceInterface . StartNetworkedVoice ( LocPlayer . ControllerId ) ;
}
}
simulated function OnDestroy ( SeqAct _Destroy Action )
{
// Kismet is not allowed to disconnect players from the game
Action . ScriptLog ( "Cannot use Destroy action on players" ) ;
}
` if( ` notdefined ( ShippingPC ) )
/** console control commands, useful when remote debugging so you can't touch the console the normal way */
exec function ConsoleKey ( name Key )
{
if ( LocalPlayer ( Player ) != None )
{
LocalPlayer ( Player ) . ViewportClient . ViewportConsole . InputKey ( 0 , Key , IE _Pressed ) ;
}
}
exec function SendToConsole ( string Command )
{
if ( LocalPlayer ( Player ) != None )
{
LocalPlayer ( Player ) . ViewportClient . ViewportConsole . ConsoleCommand ( Command ) ;
}
}
` endif
/ * *
* Iterate through list of debug text and draw it over the associated actors in world space .
* Also handles culling null entries , and reducing the duration for timed debug text .
* /
final simulated function DrawDebugTextList ( Canvas Canvas , float RenderDelta )
{
local vector CameraLoc , ScreenLoc , Offset , WorldTextLoc ;
local rotator CameraRot ;
local int Idx ;
if ( DebugTextList . Length > 0 )
{
GetPlayerViewPoint ( CameraLoc , CameraRot ) ;
Canvas . SetDrawColor ( 255 , 255 , 255 ) ;
for ( Idx = 0 ; Idx < DebugTextList . Length ; Idx ++ )
{
if ( DebugTextList [ Idx ] . SrcActor == None )
{
DebugTextList . Remove ( Idx -- , 1 ) ;
continue ;
}
if ( DebugTextList [ Idx ] . TimeRemaining != - 1. f )
{
DebugTextList [ Idx ] . TimeRemaining -= RenderDelta ;
if ( DebugTextList [ Idx ] . TimeRemaining <= 0. f )
{
DebugTextList . Remove ( Idx -- , 1 ) ;
continue ;
}
}
if ( DebugTextList [ Idx ] . Font != None )
{
Canvas . Font = DebugTextList [ Idx ] . Font ;
}
else
{
Canvas . Font = class 'Engine' . static . GetSmallFont ( ) ;
}
if ( DebugTextList [ Idx ] . bAbsoluteLocation )
{
WorldTextLoc = VLerp ( DebugTextList [ Idx ] . SrcActorOffset , DebugTextList [ Idx ] . SrcActorDesiredOffset , 1. f - ( DebugTextList [ Idx ] . TimeRemaining / DebugTextList [ Idx ] . Duration ) ) ;
}
else
{
Offset = VLerp ( DebugTextList [ Idx ] . SrcActorOffset , DebugTextList [ Idx ] . SrcActorDesiredOffset , 1. f - ( DebugTextList [ Idx ] . TimeRemaining / DebugTextList [ Idx ] . Duration ) ) ;
if ( DebugTextList [ Idx ] . bKeepAttachedToActor )
{
WorldTextLoc = DebugTextList [ Idx ] . SrcActor . Location + ( Offset >> CameraRot ) ;
}
else
{
WorldTextLoc = DebugTextList [ Idx ] . OrigActorLocation + ( Offset >> CameraRot ) ;
}
}
// don't draw text behind the camera
if ( ( ( WorldTextLoc - CameraLoc ) dot vector ( CameraRot ) ) > 0. f )
{
ScreenLoc = Canvas . Project ( WorldTextLoc ) ;
Canvas . SetPos ( ScreenLoc . X , ScreenLoc . Y ) ;
Canvas . DrawColor = DebugTextList [ Idx ] . TextColor ;
Canvas . DrawText ( DebugTextList [ Idx ] . DebugText ) ;
}
}
}
}
/ * *
* Add debug text for a specific actor to be displayed via DrawDebugTextList ( ) . If the debug text is invalid then it will
* attempt to remove any previous entries via RemoveDebugText ( ) .
* /
reliable client event AddDebugText ( string DebugText ,
optional Actor SrcActor ,
optional float Duration = - 1. f ,
optional vector Offset ,
optional vector DesiredOffset ,
optional color TextColor ,
optional bool bSkipOverwriteCheck ,
optional bool bAbsoluteLocation ,
optional bool bKeepAttachedToActor = TRUE ,
optional Font InFont
)
{
local int Idx ;
// set a default color
if ( TextColor . R == 0 && TextColor . G == 0 && TextColor . B == 0 && TextColor . A == 0 )
{
TextColor . R = 255 ;
TextColor . G = 255 ;
TextColor . B = 255 ;
TextColor . A = 255 ;
}
// and a default source actor of our pawn
if ( SrcActor != None )
{
if ( Len ( DebugText ) == 0 )
{
RemoveDebugText ( SrcActor ) ;
}
else
{
//`log("Adding debug text:"@DebugText@"for actor:"@SrcActor);
// search for an existing entry
if ( ! bSkipOverwriteCheck )
{
Idx = DebugTextList . Find ( 'SrcActor' , SrcActor ) ;
if ( Idx == INDEX _NONE )
{
// manually grow the array one struct element
Idx = DebugTextList . Length ;
DebugTextList . Length = Idx + 1 ;
}
}
else
{
Idx = DebugTextList . Length ;
DebugTextList . Length = Idx + 1 ;
}
// assign the new text and actor
DebugTextList [ Idx ] . SrcActor = SrcActor ;
DebugTextList [ Idx ] . SrcActorOffset = Offset ;
DebugTextList [ Idx ] . SrcActorDesiredOffset = DesiredOffset ;
DebugTextList [ Idx ] . DebugText = DebugText ;
DebugTextList [ Idx ] . TimeRemaining = Duration ;
DebugTextList [ Idx ] . Duration = Duration ;
DebugTextList [ Idx ] . TextColor = TextColor ;
DebugTextList [ Idx ] . bAbsoluteLocation = bAbsoluteLocation ;
DebugTextList [ Idx ] . bKeepAttachedToActor = bKeepAttachedToActor ;
DebugTextList [ Idx ] . OrigActorLocation = SrcActor . Location ;
DebugTextList [ Idx ] . Font = InFont ;
}
}
}
/ * *
* Remove debug text for the specific actor .
* /
final reliable client event RemoveDebugText ( Actor SrcActor )
{
local int Idx ;
Idx = DebugTextList . Find ( 'SrcActor' , SrcActor ) ;
if ( Idx != INDEX _NONE )
{
DebugTextList . Remove ( Idx , 1 ) ;
}
}
/ * *
* Remove all debug text
* /
final reliable client event RemoveAllDebugStrings ( )
{
DebugTextList . Length = 0 ;
}
/ * *
* Registers the host ' s stat guid with the online subsystem
*
* @ param StatGuid the stat guid to register
* /
reliable client function ClientRegisterHostStatGuid ( string StatGuid )
{
if ( OnlineSub != None && OnlineSub . StatsInterface != None )
{
OnlineSub . StatsInterface . AddRegisterHostStatGuidCompleteDelegate ( OnRegisterHostStatGuidComplete ) ;
if ( OnlineSub . StatsInterface . RegisterHostStatGuid ( StatGuid ) == false )
{
OnRegisterHostStatGuidComplete ( false ) ;
}
}
}
/ * *
* Called once the host registration has completed . Sends the host this clients stat guid
*
* @ param bWasSuccessful true if the registration worked , false otherwise
* /
function OnRegisterHostStatGuidComplete ( bool bWasSuccessful )
{
local string StatGuid ;
OnlineSub . StatsInterface . ClearRegisterHostStatGuidCompleteDelegateDelegate ( OnRegisterHostStatGuidComplete ) ;
if ( bWasSuccessful )
{
StatGuid = OnlineSub . StatsInterface . GetClientStatGuid ( ) ;
// Report the client stat guid back to the server
ServerRegisterClientStatGuid ( StatGuid ) ;
}
}
/ * *
* Registers the client ' s stat guid with the online subsystem
*
* @ param StatGuid the stat guid to register
* /
reliable server function ServerRegisterClientStatGuid ( string StatGuid )
{
if ( OnlineSub != None && OnlineSub . StatsInterface != None )
{
OnlineSub . StatsInterface . RegisterStatGuid ( PlayerReplicationInfo . UniqueId , StatGuid ) ;
}
}
/ * *
* Starts the online game using the session name in the PRI
* /
reliable client function ClientStartOnlineGame ( )
{
local OnlineGameSettings GameSettings ;
if ( OnlineSub != None &&
OnlineSub . GameInterface != None &&
IsPrimaryPlayer ( ) )
{
GameSettings = OnlineSub . GameInterface . GetGameSettings ( PlayerReplicationInfo . SessionName ) ;
// Start the game if not already in progress
if ( GameSettings != None &&
( GameSettings . GameState == OGS _Pending || GameSettings . GameState == OGS _Ended ) )
{
OnlineSub . GameInterface . StartOnlineGame ( PlayerReplicationInfo . SessionName ) ;
}
}
}
/ * *
* Ends the online game using the session name in the PRI
* /
reliable client function ClientEndOnlineGame ( )
{
local OnlineGameSettings GameSettings ;
if ( OnlineSub != None &&
OnlineSub . GameInterface != None &&
IsPrimaryPlayer ( ) )
{
GameSettings = OnlineSub . GameInterface . GetGameSettings ( PlayerReplicationInfo . SessionName ) ;
// End the game if in progress
if ( GameSettings != None &&
GameSettings . GameState == OGS _InProgress )
{
OnlineSub . GameInterface . EndOnlineGame ( PlayerReplicationInfo . SessionName ) ;
}
}
}
/** Checks for parental controls blocking user created content */
function bool CanViewUserCreatedContent ( )
{
//@HSL_BEGIN_XBOX
return bCanShareUserCreatedContent ;
//@HSL_END_XBOX
}
function IncrementNumberOfMatchesPlayed ( )
{
` log( " Num Matches Played: " $ PlayerReplicationInfo.AutomatedTestingData.NumberOfMatchesPlayed );
PlayerReplicationInfo . AutomatedTestingData . NumberOfMatchesPlayed ++ ;
}
/** For AI debugging */
event SoakPause ( Pawn P )
{
` log("Soak pause by " $ P);
SetViewTarget ( P ) ;
SetPause ( true ) ;
myHud . bShowDebugInfo = true ;
}
// AI PATHING DEBUG
exec function PathStep ( optional int Cnt )
{
Pawn . IncrementPathStep ( Max ( 1 , Cnt ) , myHud . Canvas ) ;
}
exec function PathChild ( optional int Cnt )
{
Pawn . IncrementPathChild ( Max ( 1 , Cnt ) , myHud . Canvas ) ;
}
exec function PathClear ( )
{
Pawn . ClearPathStep ( ) ;
}
/ * *
* Used when a host is telling a client to go to a specific Internet session
*
* @ param SessionName the name of the session to register
* @ param SearchClass the search that should be populated with the session
* @ param PlatformSpecificInfo the binary data to place in the platform specific areas
* /
reliable client function ClientTravelToSession ( name SessionName , class < OnlineGameSearch > SearchClass , byte PlatformSpecificInfo [ 80 ] )
{
local OnlineGameSearch Search ;
local LocalPlayer LP ;
local OnlineGameSearchResult SessionToJoin ;
LP = LocalPlayer ( Player ) ;
if ( LP != None )
{
Search = new SearchClass ;
// Get the information needed to travel to this destination
if ( OnlineSub . GameInterface . BindPlatformSpecificSessionToSearch ( LP . ControllerId , Search , PlatformSpecificInfo ) )
{
// There will be only one search result so use it
SessionToJoin = Search . Results [ 0 ] ;
` Log("(PlayerController.ClientTravelToSession): "
$ " SessionName=" $SessionName
$ " SearchClass=" $SearchClass
$ " UniqueId=" $OnlineSub . static . UniqueNetIdToString ( PlayerReplicationInfo . UniqueId )
$ " Session.OwnerId=" $OnlineSub . static . UniqueNetIdToString ( SessionToJoin . GameSettings . OwningPlayerId ) ) ;
// Fixup game settings object pre-join
PreJoinUpdateGameSettings ( SessionName , SessionToJoin . GameSettings ) ;
OnlineSub . GameInterface . AddJoinOnlineGameCompleteDelegate ( OnJoinTravelToSessionComplete ) ;
// Now join this Session
OnlineSub . GameInterface . JoinOnlineGame ( LP . ControllerId , SessionName , SessionToJoin ) ;
}
}
}
/ * *
* Last chance to fixup game settings values before joining a game with those settings
*
* @ param SessionName name of the session that is about to be joined
* @ param GameSettings settings object to be modified
* /
simulated function PreJoinUpdateGameSettings ( name SessionName , OnlineGameSettings GameSettings ) ;
/ * *
* Called when the join for the travel destination has completed
*
* @ param SessionName the name of the session the event is for
* @ param bWasSuccessful whether it worked or not
* /
function OnJoinTravelToSessionComplete ( name SessionName , bool bWasSuccessful )
{
local string URL ;
if ( bWasSuccessful )
{
// We are joining so grab the connect string to use
if ( OnlineSub . GameInterface . GetResolvedConnectString ( SessionName , URL ) )
{
` Log("Resulting url for 'Game' is (" $ URL $ ")");
// travel to the specified URL
ClientTravel ( URL , TRAVEL _Absolute ) ;
}
}
}
/ * *
* Used when a host is telling a client to return to their party host from the
* current session . It looks for the session named 'Party' and does a travel to
* it . If it ' s not available , it just does a "disconnect"
*
* @ param RequestingPlayerId net id of the player that is requesting the travel
* /
reliable client function ClientReturnToParty ( UniqueNetId RequestingPlayerId )
{
local string URL ;
// Disable when returning ourself to menus
WorldInfo . ToggleHostMigration ( false ) ;
// only do this for the first player in split-screen sessions.
if ( IsPrimaryPlayer ( ) )
{
if ( OnlineSub != None &&
OnlineSub . GameInterface != None &&
OnlineSub . PlayerInterface != None )
{
// Find the party settings to verify that a party is registered
if ( OnlineSub . GameInterface . GetGameSettings ( 'Party' ) != None )
{
// Now see if we are the party host or not
if ( IsPartyLeader ( ) )
{
// We are the party host so create the session and listen for returning clients
URL = GetPartyMapName ( ) $ "?game=" $ GetPartyGameTypeName ( ) $ "?listen" ;
// Transition to being the party host without notifying clients and traveling absolute
WorldInfo . ServerTravel ( URL , true , true ) ;
}
else
{
// We are joining so grab the connect string to use
if ( OnlineSub . GameInterface . GetResolvedConnectString ( 'Party' , URL ) )
{
ClientTravel ( URL , TRAVEL _Absolute ) ;
}
}
}
else
{
ConsoleCommand ( "disconnect" ) ;
}
}
else
{
ConsoleCommand ( "disconnect" ) ;
}
}
}
/ * *
* RPC notification to client that a party host is about to leave the match
*
* @ param PartyHostPlayerId net id of the party host that is leaving
* /
reliable client function ClientNotifyPartyHostLeaving ( UniqueNetId PartyHostPlayerId )
{
if ( PlayerReplicationInfo != None && PlayerReplicationInfo . UniqueId != PartyHostPlayerId )
{
//@todo sz - implement
` Log( ` location @ "Party host is leaving: "
$ " PartyHostPlayerId=" $class 'OnlineSubsystem' . static . UniqueNetIdToString ( PartyHostPlayerId ) ) ;
}
}
/ * *
* RPC notification to server that a party host is about to leave the match .
*
* @ param PartyHostPlayerId net id of the party host that is leaving
* /
reliable server function ServerNotifyPartyHostLeaving ( UniqueNetId PartyHostPlayerId )
{
if ( WorldInfo . Game != None )
{
// Forwarded notification to all clients
WorldInfo . Game . TellClientsPartyHostIsLeaving ( PartyHostPlayerId ) ;
}
}
/ * *
* Wrapper for determining whether this player is the first player on their console .
*
* @ return TRUE if this player is not using splitscreen , or is the first player in the split - screen layout .
* /
simulated final function bool IsPrimaryPlayer ( )
{
local int SSIndex ;
return ! IsSplitscreenPlayer ( SSIndex ) || SSIndex == 0 ;
}
/ * *
* Determines whether this player is playing split - screen .
*
* @ param out _SplitscreenPlayerIndex receives the index [ into the player ' s local GamePlayers array ] for this player , if playing splitscreen .
* .
* @ return TRUE if this player is playing splitscreen .
* /
simulated final function bool IsSplitscreenPlayer ( optional out int out _SplitscreenPlayerIndex )
{
local bool bResult ;
local LocalPlayer LP ;
local NetConnection RemoteConnection ;
local ChildConnection ChildRemoteConnection ;
out _SplitscreenPlayerIndex = NetPlayerIndex ;
if ( Player != None )
{
LP = LocalPlayer ( Player ) ;
if ( LP != None )
{
if ( LP . Outer . GamePlayers . Length > 1 )
{
out _SplitscreenPlayerIndex = LP . Outer . GamePlayers . Find ( LP ) ;
bResult = true ;
}
}
else
{
RemoteConnection = NetConnection ( Player ) ;
if ( RemoteConnection . Children . Length > 0 )
{
out _SplitscreenPlayerIndex = 0 ;
bResult = true ;
}
else
{
ChildRemoteConnection = ChildConnection ( RemoteConnection ) ;
if ( ChildRemoteConnection != None )
{
if ( ChildRemoteConnection . Parent != None )
{
out _SplitscreenPlayerIndex = ChildRemoteConnection . Parent . Children . Find ( ChildRemoteConnection ) + 1 ;
}
bResult = true ;
}
}
}
}
return bResult ;
}
/ * *
* Returns the PRI associated with the player at the specified index .
*
* @ param PlayerIndex the index [ into the local player ' s GamePlayers array ] for the player PRI to find
*
* @ return the PRI associated with the player at the specified index , or None if the player is not a split - screen player or
* the index was out of range .
* /
simulated function PlayerReplicationInfo GetSplitscreenPlayerByIndex ( optional int PlayerIndex = 1 )
{
local PlayerReplicationInfo Result ;
local LocalPlayer LP , SplitPlayer ;
local NetConnection MasterConnection , RemoteConnection ;
local ChildConnection ChildRemoteConnection ;
if ( Player != None )
{
if ( IsSplitscreenPlayer ( ) )
{
LP = LocalPlayer ( Player ) ;
RemoteConnection = NetConnection ( Player ) ;
if ( LP != None )
{
// this PC is a local player
if ( PlayerIndex >= 0 && PlayerIndex < LP . ViewportClient . Outer . GamePlayers . Length )
{
SplitPlayer = LP . ViewportClient . Outer . GamePlayers [ PlayerIndex ] ;
Result = SplitPlayer . Actor . PlayerReplicationInfo ;
}
else
{
` warn( ` location $ ":" @ "requested player at invalid index!" @ ` showvar(PlayerIndex) @ ` showvar ( LP . ViewportClient . Outer . GamePlayers . Length , NumLocalPlayers ) ) ;
}
}
else if ( RemoteConnection != None )
{
if ( WorldInfo . NetMode == NM _Client )
{
//THIS SHOULD NEVER HAPPEN - IF HAVE A REMOTECONNECTION, WE SHOULDN'T BE A CLIENT
// this player is a client
` warn( ` location $ ":" @ "CALLED ON CLIENT WITH VALID REMOTE NETCONNECTION!" ) ;
}
else
{
ChildRemoteConnection = ChildConnection ( RemoteConnection ) ;
if ( ChildRemoteConnection != None )
{
// this player controller is not the primary player in the splitscreen layout
MasterConnection = ChildRemoteConnection . Parent ;
if ( PlayerIndex == 0 )
{
Result = MasterConnection . Actor . PlayerReplicationInfo ;
}
else
{
PlayerIndex -- ;
if ( PlayerIndex >= 0 && PlayerIndex < MasterConnection . Children . Length )
{
ChildRemoteConnection = MasterConnection . Children [ PlayerIndex ] ;
Result = ChildRemoteConnection . Actor . PlayerReplicationInfo ;
}
}
}
else if ( RemoteConnection . Children . Length > 0 )
{
// this PC is the primary splitscreen player
if ( PlayerIndex == 0 )
{
// they want this player controller's PRI
Result = PlayerReplicationInfo ;
}
else
{
// our split-screen's PRI is being requested.
PlayerIndex -- ;
if ( PlayerIndex >= 0 && PlayerIndex < RemoteConnection . Children . Length )
{
ChildRemoteConnection = RemoteConnection . Children [ PlayerIndex ] ;
Result = ChildRemoteConnection . Actor . PlayerReplicationInfo ;
}
}
}
else
{
` log( ` location $ ":" @ Player @ "IS NOT THE PRIMARY CONNECTION AND HAS NO CHILD CONNECTIONS!" ) ;
}
}
}
else
{
` log( ` location $ ":" @ Player @ "IS NOT A LOCALPLAYER AND NOT A REMOTECONNECTION! (No valid Player reference)" ) ;
}
}
}
else
{
` log( ` location $ ":" @ "NULL value for Player!" ) ;
}
return Result ;
}
/ * *
* Returns the number of split - screen players playing on this player ' s machine .
*
* @ return the total number of players on the player 's local machine, or 0 if this player isn' t playing split - screen .
* /
simulated function int GetSplitscreenPlayerCount ( )
{
local LocalPlayer LP ;
local NetConnection RemoteConnection ;
local int Result ;
if ( IsSplitscreenPlayer ( ) )
{
if ( Player != None )
{
LP = LocalPlayer ( Player ) ;
RemoteConnection = NetConnection ( Player ) ;
if ( LP != None )
{
Result = LP . ViewportClient . Outer . GamePlayers . Length ;
}
else if ( RemoteConnection != None )
{
if ( ChildConnection ( RemoteConnection ) != None )
{
// we're the secondary (or otherwise) player in the split - we need to move up to the primary connection
RemoteConnection = ChildConnection ( RemoteConnection ) . Parent ;
}
// add one for the primary player
Result = RemoteConnection . Children . Length + 1 ;
}
else
{
` log( ` location @ "NOT A LOCALPLAYER AND NOT A REMOTECONNECTION!" ) ;
}
}
else
{
` log( ` location @ "called without a valid Player value!" ) ;
}
}
return Result ;
}
reliable client function ClientControlMovieTexture ( TextureMovie MovieTexture , SeqAct _ControlMovieTexture . EMovieControlType Mode )
{
if ( MovieTexture != None )
{
switch ( Mode )
{
case MCT _Play :
MovieTexture . Play ( ) ;
break ;
case MCT _Stop :
MovieTexture . Stop ( ) ;
break ;
case MCT _Pause :
MovieTexture . Pause ( ) ;
break ;
default :
break ;
}
}
}
/ * *
* Forces the streaming system to disregard the normal logic for the specified duration and
* instead always load all mip - levels for all textures used by the specified material .
*
* @ param Material - The material whose textures should be forced into memory .
* @ param ForceDuration - Number of seconds to keep all mip - levels in memory , disregarding the normal priority logic .
* @ param CinematicTextureGroups - Bitfield indicating which texture groups that use extra high - resolution mips
* /
reliable client event ClientSetForceMipLevelsToBeResident ( MaterialInterface Material , float ForceDuration , optional int CinematicTextureGroups )
{
if ( Material != None && IsPrimaryPlayer ( ) )
{
Material . SetForceMipLevelsToBeResident ( false , false , ForceDuration , CinematicTextureGroups ) ;
}
}
/ * *
* Forces the streaming system to disregard the normal logic for the specified duration and
* instead always load all mip - levels for all textures used by the specified actor .
*
* @ param ForcedActor - The actor whose textures should be forced into memory .
* @ param ForceDuration - Number of seconds to keep all mip - levels in memory , disregarding the normal priority logic .
* @ param bEnableStreaming - Whether to start ( TRUE ) or stop ( FALSE ) streaming
* @ param CinematicTextureGroups - Bitfield indicating which texture groups that use extra high - resolution mips
* /
reliable client event ClientPrestreamTextures ( Actor ForcedActor , float ForceDuration , bool bEnableStreaming , optional int CinematicTextureGroups = 0 )
{
if ( ForcedActor != None && IsPrimaryPlayer ( ) )
{
ForcedActor . PrestreamTextures ( ForceDuration , bEnableStreaming , CinematicTextureGroups ) ;
}
}
/** adds a location to the texture streaming system for the specified duration */
reliable client native final event ClientAddTextureStreamingLoc ( vector InLoc , float Duration , bool bOverrideLocation ) ;
/ * *
* Wrapper for determining whether a player is the party leader .
*
* @ return TRUE if the player is the leader of the party ; FALSE if not in a party or not the party leader .
* /
simulated function bool IsPartyLeader ( )
{
local OnlineGameSettings PartySettings ;
if ( OnlineSub != None && OnlineSub . GameInterface != None )
{
// Find the party settings to verify that a party is registered
PartySettings = OnlineSub . GameInterface . GetGameSettings ( 'Party' ) ;
if ( PartySettings != None )
{
if ( PlayerReplicationInfo != None )
{
return PartySettings . OwningPlayerId == PlayerReplicationInfo . UniqueId ;
}
}
}
return WorldInfo . NetMode != NM _Client && IsPrimaryPlayer ( ) ;
}
/ * *
* Returns the party map name for this game
* /
static function string GetPartyMapName ( ) ;
/ * *
* Returns the party game info name for this game
* /
static function string GetPartyGameTypeName ( ) ;
/ * *
* Get the completion amount for a game achievement .
*
* @ param AchievementId the id for the achievement to get the completion percetage for
* @ param CurrentValue the current number of times the event required to unlock the achievement has occurred .
* @ param MaxValue the value that represents 100 % completion .
*
* @ return TRUE if the AchievementId specified represents an progressive achievement . FALSE if the achievement is a one - time event
* or if the AchievementId specified is invalid .
* /
event bool GetAchievementProgression ( int AchievementId , out float CurrentValue , out float MaxValue ) ;
//// SENTINEL FUNCTIONS START
/** This is used to notify the PlayerController that a fly through has ended and then quit if we are doing a Sentinel run **/
simulated function OnFlyThroughHasEnded ( SeqAct _FlyThroughHasEnded InAction )
{
local PlayerController PC ;
if ( WorldInfo . Game . IsDoingASentinelRun ( ) )
{
foreach WorldInfo . AllControllers ( class 'PlayerController' , PC )
{
PC . ConsoleCommand ( "quit" ) ;
}
}
}
/ * *
* This is a function which is called when sentinel is able to start TravelTheWorld . This allows the specific game
* to do things such as turning off UI / HUD and to not enter some default starting the game state .
* * /
function Sentinel _SetupForGamebasedTravelTheWorld ( )
{
}
/ * *
* This function is called before we acquire the travel points .
* * /
function Sentinel _PreAcquireTravelTheWorldPoints ( )
{
}
/ * *
* This function is called after we acquire the travel points right before we start traveling .
* * /
function Sentinel _PostAcquireTravelTheWorldPoints ( )
{
}
//// SENTINEL FUNCTIONS END
delegate InputMatchDelegate ( )
{
}
/** Attempts to play force-feedback that matches the camera shake. To be implemented at a higher level (see GameFramework). */
simulated protected function DoForceFeedbackForScreenShake ( CameraShake ShakeData , float ShakeScale ) ;
/** Play Camera Shake */
unreliable client function ClientPlayCameraShake ( CameraShake Shake , optional float Scale = 1. f , optional bool bTryForceFeedback , optional ECameraAnimPlaySpace PlaySpace = CAPS _CameraLocal , optional rotator UserPlaySpaceRot )
{
if ( PlayerCamera != None )
{
PlayerCamera . PlayCameraShake ( Shake , Scale , PlaySpace , UserPlaySpaceRot ) ;
if ( bTryForceFeedback )
{
DoForceFeedbackForScreenShake ( Shake , Scale ) ;
}
}
}
unreliable client function ClientStopCameraShake ( CameraShake Shake )
{
if ( PlayerCamera != None )
{
PlayerCamera . StopCameraShake ( Shake ) ;
}
}
/ * *
* Scripting hook for camera shakes .
* /
function OnCameraShake ( SeqAct _CameraShake inAction )
{
if ( inAction . InputLinks [ 0 ] . bHasImpulse )
{
// start!
if ( InAction . bRadialShake )
{
// play an in-world camera shake
if ( InAction . LocationActor != None )
{
class 'Camera' . static . PlayWorldCameraShake ( InAction . Shake , inAction . LocationActor , inAction . LocationActor . Location , inAction . RadialShake _InnerRadius , inAction . RadialShake _OuterRadius , InAction . RadialShake _Falloff , InAction . bDoControllerVibration , inAction . bOrientTowardRadialEpicenter ) ;
}
else
{
` Warn(self@"Location actor needed for bRadialFalloff camera shake.");
return ;
}
}
else
{
// just shake Target(s)
ClientPlayCameraShake ( inAction . Shake , inAction . ShakeScale ,
inAction . bDoControllerVibration , inAction . PlaySpace ,
( inAction . LocationActor == None ) ? rot ( 0 , 0 , 0 ) : inAction . LocationActor . Rotation ) ;
}
}
else
{
// stop!
ClientStopCameraShake ( inAction . Shake ) ;
}
}
unreliable client event ClientPlayCameraAnim ( CameraAnim AnimToPlay , optional float Scale = 1. f , optional float Rate = 1. f ,
optional float BlendInTime , optional float BlendOutTime , optional bool bLoop ,
optional bool bRandomStartTime , optional ECameraAnimPlaySpace Space = CAPS _CameraLocal , optional rotator CustomPlaySpace )
{
local CameraAnimInst AnimInst ;
if ( PlayerCamera != None )
{
AnimInst = PlayerCamera . PlayCameraAnim ( AnimToPlay , Rate , Scale , BlendInTime , BlendOutTime , bLoop , bRandomStartTime ) ;
if ( AnimInst != None && Space != CAPS _CameraLocal )
{
AnimInst . SetPlaySpace ( Space , CustomPlaySpace ) ;
}
}
}
reliable client event ClientStopCameraAnim ( CameraAnim AnimToStop , optional bool bImmediate )
{
if ( PlayerCamera != None )
{
PlayerCamera . StopAllCameraAnimsByType ( AnimToStop , bImmediate ) ;
}
}
/** Toggle camera animation debug output */
exec function DebugCameraAnims ( )
{
bDebugCameraAnims = ! bDebugCameraAnims ;
if ( bDebugCameraAnims == FALSE )
{
// clear debug lines
WorldInfo . FlushPersistentDebugLines ( ) ;
}
}
/** Spawn a camera lens effect (e.g. blood).*/
unreliable client event ClientSpawnCameraLensEffect ( class < EmitterCameraLensEffectBase > LensEffectEmitterClass )
{
if ( PlayerCamera != None )
{
PlayerCamera . AddCameraLensEffect ( LensEffectEmitterClass ) ;
}
}
/** Remove a camera lens effect (e.g. blood).*/
unreliable client event ClientRemoveCameraLensEffect ( class < EmitterCameraLensEffectBase > LensEffectEmitterClass )
{
Local EmitterCameraLensEffectBase EmitterLensEffect ;
if ( PlayerCamera != None )
{
EmitterLensEffect = PlayerCamera . FindCameraLensEffect ( LensEffectEmitterClass ) ;
if ( EmitterLensEffect != none )
{
PlayerCamera . RemoveCameraLensEffect ( EmitterLensEffect ) ;
EmitterLensEffect . Destroy ( ) ;
}
}
}
/** A trivial way to handle the SetSoundMode kismet action */
function OnSetSoundMode ( SeqAct _SetSoundMode Action )
{
local AudioDevice Audio ;
Audio = class 'Engine' . static . GetAudioDevice ( ) ;
if ( Audio != None )
{
if ( Action . InputLinks [ 0 ] . bHasImpulse && ( Action . SoundMode != None ) )
{
Audio . SetSoundMode ( Action . SoundMode . Name ) ;
}
else
{
Audio . SetSoundMode ( 'Default' ) ;
}
}
}
/ * *
* Determine if this player has a peer connection for the given net id
*
* @ param PeerNetId net id of remote client peer
* @ return TRUE if the player has the peer connection
* /
native function bool HasPeerConnection ( const out UniqueNetId PeerNetId ) const ;
/ * *
* This will move the player and set their rotation to the passed in values .
* We have this version of the BugIt family as it is easier to type in just raw numbers in the console .
* * /
exec function BugItGo ( coerce float X , coerce float Y , coerce float Z , coerce int Pitch , coerce int Yaw , coerce int Roll )
{
local vector TheLocation ;
local rotator TheRotation ;
TheLocation . X = X ;
TheLocation . Y = Y ;
TheLocation . Z = Z ;
TheRotation . Pitch = Pitch ;
TheRotation . Yaw = Yaw ;
TheRotation . Roll = Roll ;
BugItWorker ( TheLocation , TheRotation ) ;
}
/ * *
* This will move the player and set their rotation to the passed in values .
* We have this version of the BugIt family strings can be passed in from the game ? options easily
* * /
function BugItGoString ( string TheLocation , string TheRotation )
{
BugItWorker ( vector ( TheLocation ) , rotator ( TheRotation ) ) ;
}
/ * *
* This will move the player and set their rotation to the passed in values .
* This actually does the location / rotation setting . Additionally it will set you as ghost as the level may have
* changed since the last time you were here . And the bug may actually be inside of something .
* * /
function BugItWorker ( vector TheLocation , rotator TheRotation )
{
` log( "BugItGo to:" @ TheLocation @ TheRotation );
if ( CheatManager != none )
{
CheatManager . Ghost ( ) ;
}
ViewTarget . SetLocation ( TheLocation ) ;
` if( ` _ _TW _ ) // Just avoiding an accessed none if in debugcameracontroller mode where you have no pawn
if ( Pawn != none )
{
Pawn . FaceRotation ( TheRotation , 0.0 f ) ;
}
` else
Pawn . FaceRotation ( TheRotation , 0.0 f ) ;
` endif
SetRotation ( TheRotation ) ;
}
/ * *
* This function is used to print out the BugIt location . It prints out copy and paste versions for both IMing someone to type in
* and also a gameinfo ? options version so that you can append it to your launching url and be taken to the correct place .
* Additionally , it will take a screen shot so reporting bugs is a one command action !
*
* * /
exec event BugIt ( optional string ScreenShotDescription )
{
local vector ViewLocation ;
local rotator ViewRotation ;
local String GoString ;
local String LocString ;
if ( ! class 'WorldInfo' . static . IsConsoleBuild ( CONSOLE _Orbis ) )
{
ConsoleCommand ( "bugscreenshot " $ ScreenShotDescription ) ;
}
else
{
` log("BUGIT: USE THE PS4'S SCRRENSHOT FUNCITONALITY!");
}
GetPlayerViewPoint ( ViewLocation , ViewRotation ) ;
if ( Pawn != None )
{
ViewLocation = Pawn . Location ;
}
BugItStringCreator ( ViewLocation , ViewRotation , GoString , LocString ) ;
LogOutBugItGoToLogFile ( ScreenShotDescription , GoString , LocString ) ;
}
/ * *
* Logs the current location in bugit format without taking screenshot and further routing .
* /
exec function LogLoc ( )
{
local vector ViewLocation ;
local rotator ViewRotation ;
local String GoString ;
local String LocString ;
GetPlayerViewPoint ( ViewLocation , ViewRotation ) ;
if ( Pawn != None )
{
ViewLocation = Pawn . Location ;
}
BugItStringCreator ( ViewLocation , ViewRotation , GoString , LocString ) ;
}
exec event BugItAI ( optional string ScreenShotDescription )
{
local vector ViewLocation ;
local rotator ViewRotation ;
local String GoString ;
local String LocString ;
GetPlayerViewPoint ( ViewLocation , ViewRotation ) ;
` if( ` _ _TW _ )
if ( Pawn ( ViewTarget ) != none )
{
ViewLocation = ViewTarget . Location ;
}
else
` endif
if ( Pawn != None )
{
ViewLocation = Pawn . Location ;
}
BugItStringCreator ( ViewLocation , ViewRotation , GoString , LocString ) ;
ConsoleCommand ( "debugai" ) ;
SetTimer ( 0.1 , FALSE , nameof ( DisableDebugAI ) ) ;
LogOutBugItAIGoToLogFile ( ScreenShotDescription , GoString , LocString ) ;
}
/** This will create a BugItGo string for us. Nice for calling form c++ where you just want the string and no Screenshots **/
exec event BugItStringCreator ( const out Vector ViewLocation , const out Rotator ViewRotation , out String GoString , out String LocString )
{
GoString = "BugItGo " $ ViewLocation . X $ " " $ ViewLocation . Y $ " " $ ViewLocation . Z $ " " $ ViewRotation . Pitch $ " " $ ViewRotation . Yaw $ " " $ ViewRotation . Roll ;
` log( GoString );
LocString = "?BugLoc=" $ string ( ViewLocation ) $ "?BugRot=" $ string ( ViewRotation ) ;
` log( LocString );
}
/** An event that is called after the first tick of GEngine */
event OnEngineInitialTick ( )
{
// by default, we need to hide any loading movies at this point (a subclass
// can override this to keep the movie up longer)
if ( WorldInfo . IsConsoleBuild ( CONSOLE _Mobile ) )
{
ConsoleCommand ( "mobile stoploading" ) ;
}
}
` if( ` notdefined ( FINAL _RELEASE ) )
/ * *
* Logs the list of active PRIs in the game
* /
function DebugLogPRIs ( )
{
local int PlayerIndex ;
local UniqueNetId NetId ;
// Log the PRI information for comparison with the online session state
if ( WorldInfo != None &&
WorldInfo . GRI != None )
{
` Log(" Number of PRI players: " $ WorldInfo.GRI.PRIArray.Length);
// List the PRIs that are here to find mismatches between the online state
for ( PlayerIndex = 0 ; PlayerIndex < WorldInfo . GRI . PRIArray . Length ; PlayerIndex ++ )
{
NetId = WorldInfo . GRI . PRIArray [ PlayerIndex ] . UniqueId ;
` Log(" Player: " $ WorldInfo.GRI.PRIArray[PlayerIndex].PlayerName $ " UID (" $ class'OnlineSubsystem'.static.UniqueNetIdToString(NetId) $ ") PC (" $ WorldInfo.GRI.PRIArray[PlayerIndex].Owner $ ")");
}
` Log("");
}
}
/ * *
* Logs the current session state for the online layer
* /
exec function DumpOnlineSessionState ( )
{
if ( CheatManager != None )
{
CheatManager . DumpOnlineSessionState ( ) ;
}
else
{
// Log PRI player info
DebugLogPRIs ( ) ;
// Log the online session state
if ( OnlineSub != None )
{
OnlineSub . DumpSessionState ( ) ;
}
}
}
/ * *
* Logs the current muting state of the server
* /
exec function DumpVoiceMutingState ( )
{
local UniqueNetId NetId ;
local PlayerController PC ;
local int MuteIndex ;
` Log("");
` Log("Voice state");
` Log("-------------------------------------------------------------");
` Log("");
// Log the online view of the voice state
if ( OnlineSub != None )
{
OnlineSub . DumpVoiceRegistration ( ) ;
}
` Log("Muting state");
// For each player list their gameplay mutes and system wide mutes
foreach WorldInfo . AllControllers ( class 'PlayerController' , PC )
{
` Log(" Player: " $ PC.PlayerReplicationInfo.PlayerName);
` Log(" Gameplay mutes: ");
for ( MuteIndex = 0 ; MuteIndex < PC . GameplayVoiceMuteList . Length ; MuteIndex ++ )
{
NetId = PC . GameplayVoiceMuteList [ MuteIndex ] ;
` Log(" " $ class'OnlineSubsystem'.static.UniqueNetIdToString(NetId));
}
` Log(" System mutes: ");
for ( MuteIndex = 0 ; MuteIndex < PC . VoiceMuteList . Length ; MuteIndex ++ )
{
NetId = PC . VoiceMuteList [ MuteIndex ] ;
` Log(" " $ class'OnlineSubsystem'.static.UniqueNetIdToString(NetId));
}
` Log(" Voice packet filter: ");
for ( MuteIndex = 0 ; MuteIndex < PC . VoicePacketFilter . Length ; MuteIndex ++ )
{
NetId = PC . VoicePacketFilter [ MuteIndex ] ;
` Log(" " $ class'OnlineSubsystem'.static.UniqueNetIdToString(NetId));
}
` Log("");
}
}
/ * *
* Logs the list of active peer connections for this player
* /
exec function DumpPeers ( )
{
local UniqueNetId NetId ;
local PlayerController PC ;
local int PeerIdx ;
local ConnectedPeerInfo PeerInfo ;
` Log("");
` Log("Peer List");
foreach WorldInfo . AllControllers ( class 'PlayerController' , PC )
{
if ( WorldInfo . NetMode == NM _Client || ! PC . IsLocalPlayerController ( ) )
{
` Log(" Player: " $ PC.PlayerReplicationInfo.PlayerName $ "(" $ class'OnlineSubsystem'.static.UniqueNetIdToString(PC.PlayerReplicationInfo.UniqueId) $ ")");
` Log(" Peer connections: ");
for ( PeerIdx = 0 ; PeerIdx < PC . ConnectedPeers . Length ; PeerIdx ++ )
{
PeerInfo = PC . ConnectedPeers [ PeerIdx ] ;
` Log(" " $ class'OnlineSubsystem'.static.UniqueNetIdToString(PeerInfo.PlayerId)
// $" NAT="$PeerInfo.NATType
$ " HostLost=" $PeerInfo . bLostConnectionToHost ) ;
}
` Log(" Best Hosts:");
for ( PeerIdx = 0 ; PeerIdx < PC . BestNextHostPeers . Length ; PeerIdx ++ )
{
NetId = PC . BestNextHostPeers [ PeerIdx ] ;
` Log(" " $ class'OnlineSubsystem'.static.UniqueNetIdToString(NetId));
}
` Log("");
}
}
}
//@HSL_BEGIN_XBOX
exec function SetCallback ( )
{
OnlineSub . GameInterface . AddJoinOnlineGameCompleteDelegate ( OnInviteJoinComplete ) ;
}
function OnFriendsReadComplete ( bool bWasSuccessful )
{
local OnlinePlayerInterface PlayerInterface ;
local array < OnlineFriend > FriendsList ;
if ( bWasSuccessful == true )
{
// Figure out if we have an online subsystem registered
if ( OnlineSub != None )
{
// Grab the player interface to verify the subsystem supports it
PlayerInterface = OnlineSub . PlayerInterface ;
if ( PlayerInterface != None )
{
// Make a copy of the friends data for the UI
PlayerInterface . GetFriendsList ( 0 , FriendsList ) ;
}
}
}
else
{
` Log("Failed to read friends list",,'DevOnline');
}
OnlineSub . PlayerInterface . ClearReadFriendsCompleteDelegate ( 0 , OnFriendsReadComplete ) ;
}
exec function ReadFriendsList ( )
{
local OnlinePlayerInterface PlayerInterface ;
if ( OnlineSub != None )
{
PlayerInterface = OnlineSub . PlayerInterface ;
if ( PlayerInterface != None )
{
PlayerInterface . AddReadFriendsCompleteDelegate ( 0 , OnFriendsReadComplete ) ;
PlayerInterface . ReadFriendsList ( 0 ) ;
}
}
}
//@HSL_END_XBOX
` endif
function DisableDebugAI ( )
{
ConsoleCommand ( "debugai" ) ;
}
private native function LogOutBugItGoToLogFile ( const String InScreenShotDesc , const string InGoString , const string InLocString ) ;
private native function LogOutBugItAIGoToLogFile ( const String InScreenShotDesc , const string InGoString , const string InLocString ) ;
/ * *
* This function will be called to notify the player controller that the world has received it ' s game class . In the case of a client
* we need to initialize the Input System here .
*
* @ Param GameClass - The Class of the game that was replicated
* /
simulated function ReceivedGameClass ( class < GameInfo > GameClass )
{
if ( PlayerInput != none )
{
PlayerInput . ClientInitInputSystem ( ) ;
}
}
/ * *
* Triggered when the 'disconnect' console command is called , to allow cleanup before disconnecting ( e . g . for the online subsystem )
* NOTE : If you block disconnect , store the 'Command' parameter , and trigger ConsoleCommand ( Command ) when done ; be careful to avoid recursion
*
* @ param Command The command which triggered disconnection , e . g . "disconnect" or "disconnect local" ( can parse additional parameters here )
* @ return Return True to block the disconnect command from going through , if cleanup can ' t be completed immediately
* /
event bool NotifyDisconnect ( string Command )
{
return false ;
}
/** Function called from matinee to turn on/off a constant camera anim for noise */
event SetMatineeConstantCameraAnim ( bool bOn , byte Type , float Rate ) ;
event SetUIScale ( float fScale ) ;
` if( ` _ _TW _ONLINESUBSYSTEM _ )
/** see TWOnlineLobby */
function NotifyUnsuccessfulSearch ( ) ;
//To notify that the user has entered or left a party so that we make handle this in KFPlayerController
function OnLobbyStatusChanged ( bool bInLobby ) ;
` endif //( ` _ _TW _ONLINESUBSYSTEM _ )
` if( ` _ _TW _ )
/** KFPawn::IsNetRelevantFor() */
unreliable client event ClientReplicationDebug ( vector CamLocation , vector DebugLocation , bool bClear , color DebugColor )
{
` if( ` notdefined ( ShippingPC ) )
if ( bClear )
{
FlushPersistentDebugLines ( ) ;
}
DrawDebugSphere ( DebugLocation , 8 , 8 , DebugColor . R , DebugColor . G , DebugColor . B , True ) ;
DrawDebugLine ( CamLocation , DebugLocation , DebugColor . R , DebugColor . G , DebugColor . B , TRUE ) ;
` endif
}
` endif //( ` _ _TW _ )
//@HSL_BEGIN_XBOX
simulated event SendReliableVoicePacketToServer ( byte MessageType , UniqueNetId Receiver , int Length , byte InData [ 60 ] )
{
ServerReceiveReliableVoicePacket ( MessageType , Receiver , Length , InData ) ;
}
reliable server function ServerReceiveReliableVoicePacket ( byte MessageType , UniqueNetId Receiver , int Length , byte InData [ 60 ] )
{
local UniqueNetId ZeroId ;
local int i ;
if ( Receiver != ZeroId )
{
for ( i = 0 ; i < WorldInfo . GRI . PRIArray . Length ; i ++ )
{
if ( WorldInfo . GRI . PRIArray [ i ] . UniqueId == Receiver )
{
` log("Sending reliable voice packet of message type"@MessageType@"to"@WorldInfo.GRI.PRIArray[i].PlayerName);
PlayerController ( WorldInfo . GRI . PRIArray [ i ] . Owner ) . ClientReceiveReliableVoicePacket ( MessageType , PlayerReplicationInfo . UniqueId , Length , InData ) ;
return ;
}
}
` log("COULD NOT FIND RECIEVER"@class'OnlineSubsystem'.static.UniqueNetIdToString( Receiver, false )@"FOR RELIABLE VOICE PACKET. CLIENT WILL NOT RECIEVE IT!");
}
else
{
` log("Sending reliable voice packet of message type"@MessageType);
for ( i = 0 ; i < WorldInfo . GRI . PRIArray . Length ; i ++ )
{
PlayerController ( WorldInfo . GRI . PRIArray [ i ] . Owner ) . ClientReceiveReliableVoicePacket ( MessageType , PlayerReplicationInfo . UniqueId , Length , InData ) ;
}
}
}
reliable client function ClientReceiveReliableVoicePacket ( byte MessageType , UniqueNetId Sender , int Length , byte InData [ 60 ] )
{
OnlineSub . VoiceInterface . ReceiveReliableVoicePacket ( MessageType , Sender , Length , InData ) ;
}
//@HSL_END_XBOX
defaultproperties
{
// The PlayerController is currently required to have collision as there is code that uses the collision
// for moving the spectator camera around. Without collision certain assumptions will not hold (e.g. the
// spectator does not have a pawn so the various game code looks at ALL pawns. Having a new pawnType will
// require changes to that code.
// Removing the collision from the controller and using the controller - pawn mechanic will eventually be coded
// for spectator-esque functionality
Begin Object Class = CylinderComponent Name = CollisionCylinder
CollisionRadius = + 22.000000
CollisionHeight = + 22.000000
End Object
CollisionComponent = CollisionCylinder
CylinderComponent = CollisionCylinder
Components . Add ( CollisionCylinder )
bInteractiveMode = true
FOVAngle = 85.000
NetPriority = 3
bIsPlayer = true
bCanDoSpecial = true
Physics = PHYS _None
CheatClass = class 'Engine.CheatManager'
InputClass = class 'Engine.PlayerInput'
CameraClass = class 'Camera'
MaxResponseTime = 0.125
ClientCap = 0
LastSpeedHackLog = - 100.0
SavedMoveClass = class 'SavedMove'
DesiredFOV = 85.000000
DefaultFOV = 85.000000
LODDistanceFactor = 1.0
bCinemaDisableInputMove = false
bCinemaDisableInputLook = false
bIsUsingStreamingVolumes = TRUE
SpectatorCameraSpeed = 600.0
MinRespawnDelay = 1.0
//@SABER_EGS_BEGIN Crossplay support
bIsEosPlayer = false
//@SABER_EGS_END
}