//=============================================================================
// 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().
//=============================================================================
class PlayerController extends Controller
    config(user)
    native
    nativereplication
    exportstructs
	DependsOn(Interactions);

// Player info.
var const player Player;

// player input control
var globalconfig    bool    bLookUpStairs;  // look up/down stairs (player)
var globalconfig    bool    bSnapToLevel;   // Snap to level eyeheight when not mouselooking
var globalconfig    bool    bAlwaysMouseLook;
var globalconfig    bool    bKeyboardLook;  // no snapping when true
var bool                    bCenterView;

// Player control flags
var bool        bBehindView;    // Outside-the-player view.
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        bIsTyping;
var bool        bFixedCamera;   // used to fix camera in position (to view animations)
var bool        bJumpStatus;    // used in net games
var bool        bUpdating;
var globalconfig bool   bNeverSwitchOnPickup;   // if true, don't automatically switch to picked up weapon
var bool		bHideSpectatorBeacons;
var bool        bZooming;
var	bool		bHideVehicleNoEntryIndicator;

var globalconfig bool bAlwaysLevel;
var bool        bSetTurnRot;
var bool        bCheatFlying;   // instantly stop in flying mode
var bool        bFreeCamera;    // free camera when in behindview mode (for checking out player models and animations)
var bool        bZeroRoll;
var bool        bCameraPositionLocked;
var	bool		bViewBot;
var bool		UseFixedVisibility;

var bool    bFreeCam;               // In FreeCam mode to adjust the cam rotator
var bool    bFreeCamZoom;           // In zoom mode
var bool    bFreeCamSwivel;         // In swivel mode
var bool	bBlockCloseCamera;
var bool	bValidBehindCamera;
var bool	bForcePrecache;
var bool	bClientDemo;
var const bool bAllActorsRelevant;	// used by UTTV.  DO NOT SET THIS TRUE - it has a huge impact on network performance
var bool	bShortConnectTimeOut;	// when true, reduces connect timeout to 15 seconds
var bool	bPendingDestroy;		// when true, playercontroller is being destroyed
var	bool	bEnableAmbientShake;

var globalconfig bool bNoVoiceMessages;
var globalconfig bool bNoTextToSpeechVoiceMessages;
var globalconfig bool bNoVoiceTaunts;
var globalconfig bool bNoAutoTaunts;
var globalconfig bool bAutoTaunt;
var globalconfig bool bNoMatureLanguage;
var globalconfig bool bDynamicNetSpeed;
var globalconfig bool bSmallWeapons;
var bool bWeaponViewShake;
var globalconfig bool bLandingShake;
var globalconfig bool bAimingHelp;

var(ForceFeedback) globalconfig bool bEnablePickupForceFeedback;
var(ForceFeedback) globalconfig bool bEnableWeaponForceFeedback;
var(ForceFeedback) globalconfig bool bEnableDamageForceFeedback;
var(ForceFeedback) globalconfig bool bEnableGUIForceFeedback;
var(ForceFeedback) bool bForceFeedbackSupported;  // true if a device is detected

var(VoiceChat)               bool           bVoiceChatEnabled;	    // Whether voice chat is enabled on this client
var(VoiceChat)  globalconfig bool           bEnableInitialChatRoom; // Enables speaking on DefaultActiveChannel upon joining server
var	bool									bViewingMatineeCinematic;
var bool									bCustomListener;
var bool									bAcuteHearing;			// makes playercontroller hear much better (used to magnify hit sounds caused by player)

var bool bMenuBeforeRespawn; //forces the midgame menu to pop up before player can click to respawn

var bool  bSkippedLastUpdate, bLastPressedJump;

var globalconfig bool bEnableStatsTracking;
var globalconfig bool bOnlySpeakTeamText;

var bool bWasSpeedHack;
var bool bIsSpaceFighter;	// hack for spacefighter joystick controls
var const bool bWasSaturated;		// used by servers to identify saturated client connections

var float FOVBias;

// Voice Chat
struct StoredChatPassword
{
	var string ChatRoomName;
	var string ChatRoomPassword;
};
// Contains a bit-mask of which channels to auto-join
// 1 - Public, 2 - Local, 4 - Team
var(VoiceChat)  globalconfig byte			AutoJoinMask;

var input byte
    bStrafe, bSnapLevel, bLook, bFreeLook, bTurn180, bTurnToNearest, bXAxis, bYAxis;

var EDoubleClickDir DoubleClickDir;     // direction of movement key double click (for special moves)

var globalconfig byte AnnouncerLevel;        // 0=none, 1=no possession announcements, 2=all
var globalconfig byte AnnouncerVolume;       // 1 to 4

var globalconfig float	TextToSpeechVoiceVolume;

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 input float
    aBaseX, aBaseY, aBaseZ, aMouseX, aMouseY,
    aForward, aTurn, aStrafe, aUp, aLookUp;

// Vehicle Move Replication
var float aLastForward, aLastStrafe, aLastUp, NumServerDrives, NumSkips;

// if _RO_
var		rotator		WeaponBufferRotation;
// end _RO

// Vehicle Check Radius
var float   VehicleCheckRadius;         // Radius that is checked for nearby vehicles when pressing use
var bool	bSuccessfulUse;				// gives PC a hint that UsedBy was successful

// Camera info.
var int ShowFlags;
var int Misc1,Misc2;
var int RendMap;
var float        OrthoZoom;     // Orthogonal/map view zoom factor.
var const actor ViewTarget;
var const Controller RealViewTarget;
var PlayerController DemoViewer;
var float CameraDist;       // multiplier for behindview camera dist
var range CameraDistRange;
var vector OldCameraLoc;		// used in behindview calculations
var rotator OldCameraRot;
var transient array<CameraEffect> CameraEffects;    // A stack of camera effects.

// We don't want these as configs. It makes it too easy to hack
// if _RO_
var float DesiredFOV;
var float DefaultFOV;
//else
//var globalconfig float DesiredFOV;
//var globalconfig float DefaultFOV;
// end _RO
var float       ZoomLevel, DesiredZoomLevel;

// Audio.
var vector ListenerLocation;
var rotator ListenerRotation;

// Fixed visibility.
var vector	FixedLocation;
var rotator	FixedRotation;
var matrix	RenderWorldToCamera;

// Screen flashes
var vector FlashScale, FlashFog;
var float ConstantGlowScale;
var vector ConstantGlowFog;
var globalconfig float ScreenFlashScaling;

// Distance fog fading.
var color	LastDistanceFogColor;
var float	LastDistanceFogStart;
var float	LastDistanceFogEnd;
var float	CurrentDistanceFogEnd;
var float	TimeSinceLastFogChange;
var int		LastZone;

// Remote Pawn ViewTargets
var rotator     TargetViewRotation;
var rotator     BlendedTargetViewRotation;
var float       TargetEyeHeight;
var vector      TargetWeaponViewOffset;

var HUD myHUD;  // heads up display info

var float LastPlaySound;
var float LastPlaySpeech;

// Music info.
var string              Song;
var EMusicTransition    Transition;

// Move buffering for network games.  Clients save their un-acknowledged moves in order to replay them
// when they get position updates from the server.
var SavedMove SavedMoves;   // buffered moves pending position updates
var SavedMove FreeMoves;    // freed moves, available for buffering
var SavedMove PendingMove;
var float CurrentTimeStamp,LastUpdateTime,ServerTimeStamp,TimeMargin, ClientUpdateTime;
var float MaxTimeMargin;
var globalconfig float TimeMarginSlack; // OBSOLETE
var Weapon OldClientWeapon;
var int WeaponUpdate;

// Progess Indicator - used by the engine to provide status messages (HUD is responsible for displaying these).
var string  ProgressMessage[4];
var color   ProgressColor[4];
var float   ProgressTimeOut;

// Localized strings
var localized string QuickSaveString;
var localized string NoPauseMessage;
var localized string ViewingFrom;
var localized string OwnCamera;

// ReplicationInfo
var GameReplicationInfo			GameReplicationInfo;
var VoiceChatReplicationInfo	VoiceReplicationInfo;
var VotingReplicationInfoBase   VoteReplicationInfo;

// Stats Logging
var globalconfig string StatsUsername;
var globalconfig string StatsPassword;

var class<LocalMessage> LocalMessageClass;
var(VoiceChat) class<ChatRoomMessage> ChatRoomMessageClass;

// view shaking (affects roll, and offsets camera position)
var vector  ShakeOffsetRate;
var vector  ShakeOffset; //current magnitude to offset camera from shake
var vector  ShakeOffsetTime;
var vector  ShakeOffsetMax;
var vector  ShakeRotRate;
var vector  ShakeRotMax;
var rotator ShakeRot;
var vector  ShakeRotTime;

var	float		AmbientShakeFalloffStartTime;
var float		AmbientShakeFalloffTime; // Time taken for shaking to stop after AmbientShakeFalloffStartTime has passed.
var vector		AmbientShakeOffsetMag;
var float		AmbientShakeOffsetFreq;
var rotator		AmbientShakeRotMag;
var float		AmbientShakeRotFreq;

var Pawn        TurnTarget;
var config int  EnemyTurnSpeed;
var int         GroundPitch;
var rotator     TurnRot180;

var vector OldFloor;        // used by PlayerSpider mode - floor for which old rotation was based;

// Components ( inner classes )
var private transient CheatManager    CheatManager;   // Object within playercontroller that manages "cheat" commands
var class<CheatManager>               CheatClass;     // class of my CheatManager
var private transient PlayerInput     PlayerInput;    // Object within playercontroller that manages player input.
var config class<PlayerInput>         InputClass;     // class of my PlayerInput
var private transient AdminBase		  AdminManager;
var transient MaplistManagerBase      MapHandler;     // Used by AdminBase
var string                            PlayerChatType;
var PlayerChatManager				  ChatManager;    // Manages all chat, speech, and voice messages sent to player
var const vector FailedPathStart;

// Camera control for debugging/tweaking

// BehindView Camera Adjustments
var rotator CameraDeltaRotation;    // The rotator delta adjustment
var float   CameraDeltaRad;         // The zoom delta adjustment
var rotator CameraSwivel;           // The swivel adjustment

// For drawing player names
struct PlayerNameInfo
{
    var string mInfo;
    var color  mColor;
    var float  mXPos;
    var float  mYPos;
};

var(TeamBeacon) float      TeamBeaconMaxDist;
var(TeamBeacon) float      TeamBeaconPlayerInfoMaxDist;
var(TeamBeacon) Texture    TeamBeaconTexture;
var(TeamBeacon) Texture    LinkBeaconTexture;
var(TeamBeacon) Texture    SpeakingBeaconTexture;
var(TeamBeacon) Color      TeamBeaconTeamColors[2];
var(TeamBeacon) Color      TeamBeaconCustomColor;

var private const array<PlayerNameInfo> PlayerNameArray;

// Demo recording view rotation
var int DemoViewPitch;
var int DemoViewYaw;

var Security PlayerSecurity;	// Used for Cheat Protection

var float LoginDelay;
var float NextLoginTime;
var float ForcePrecacheTime;

var float LastPingUpdate;
var float ExactPing;
var float OldPing;
var float SpectateSpeed;
var globalconfig float DynamicPingThreshold;
var float NextSpeedChange;
var float VoiceChangeLimit;
var int ClientCap;

var(Menu)	config string	MidGameMenuClass;	// Menu that is shown when Escape is pressed
var(Menu)	config string   DemoMenuClass;		// Menu used for demos
var(Menu)   config string	AdminMenuClass;	    // Menu that is shown when adminmenu command is used
var(Menu)   config string   ChatPasswordMenuClass;	// Menu that appears when attempting to join a chatroom that has a password


var(VoiceChat) globalconfig array<StoredChatPassword> 	StoredChatPasswords;
var				VoiceChatRoom				ActiveRoom;			// The chatroom we're currently speaking to
var(VoiceChat)  globalconfig string         LastActiveChannel;	// Stores the currently active channel when switching maps
var(VoiceChat)  globalconfig string         VoiceChatCodec;     // Which voice chat codec to request in internet games (will only be used if it exists on server)
var(VoiceChat)  globalconfig string         VoiceChatLANCodec;  // Which voice chat codec to request in LAN games

var(VoiceChat)	globalconfig string 		ChatPassword;		// Password for our personal chat room
var(VoiceChat)  globalconfig string         DefaultActiveChannel;	// Channel we initially want to make active

// ClientAdjustPosition replication (event called at end of frame)
struct ClientAdjustment
{
    var float TimeStamp;
    var name newState;
    var EPhysics newPhysics;
    var vector NewLoc;
    var vector NewVel;
    var actor NewBase;
    var vector NewFloor;
};
var ClientAdjustment PendingAdjustment;

var	AnnouncerQueueManager		AnnouncerQueueManager;	// Handling Announcer Queueing
var AnnouncerVoice StatusAnnouncer;
var AnnouncerVoice RewardAnnouncer;

var float LastActiveTime;		// used to kick idlers

var Actor	CalcViewActor;		// optimize PlayerCalcView
var vector	CalcViewActorLocation;
var vector	CalcViewLocation;
var rotator	CalcViewRotation;
var float	LastPlayerCalcView;

var float LastBroadcastTime;
var string LastBroadcastString[4];
var float LastSpeedHackLog;

var string PlayerOwnerName;	// for savegames

// if _RO_
// Motion Blur vars
var		float		BlurTime;         				// How long blur effect should last
var		float		ColorFadeTime;         			// How long blur effect should last

// Spectating stuff
var		bool							bLockedBehindView;	// Whether this player is in locked chase mode or not. Set by the game class
var		bool							bFirstPersonSpectateOnly;	// Whether this player can spectate in third person. Set by the game class
var		bool							bAllowRoamWhileSpectating;  // Whether this player can roam around while spectating. Set by the game class
var		bool							bViewBlackWhenDead;         // Force a blacked out view when the player is dead spectating
var		bool							bViewBlackOnDeadNotViewingPlayers;  // Force a blacked out view when the player is dead spectating and not viewing another player
var		bool							bAllowRoamWhileDeadSpectating;  // Whether this player can roam around while spectating dead. Set by the game class

// Steam
var	class<SteamStatsAndAchievementsBase>	SteamStatsAndAchievementsClass;	// Stats and Achievements class setting
var	SteamStatsAndAchievementsBase			SteamStatsAndAchievements;		// Created on the Server and replicated to the Client to allow Steam Stats to be controlled by the Server but sent to Steam by the Client

// Steam Workshop
var	int		TotalSubscribedFiles;
var	int		NextSubscribedFileToFetch;
var	int		SubscribedFileDownloadIndex;
var string	SubscribedFileDownloadTitle;
var float   DownloadFileProgress;
// end _RO_

replication
{
    // Things the server should send to the client.
    reliable if( bNetDirty && bNetOwner && Role==ROLE_Authority )
        GameReplicationInfo, VoiceReplicationInfo, ChatManager, LoginDelay;
    unreliable if ( bNetOwner && Role==ROLE_Authority && (ViewTarget != Pawn) && (Pawn(ViewTarget) != None) )
        TargetViewRotation, TargetEyeHeight;
    reliable if( bDemoRecording && Role==ROLE_Authority )
        DemoViewPitch, DemoViewYaw;

    // Functions server can call.
    reliable if( Role==ROLE_Authority )
        ClientSetHUD,ClientReliablePlaySound, FOV, StartZoom,
        ToggleZoom, StopZoom, EndZoom, ClientSetMusic, ClientRestart, ClientReset,
        ClientAdjustGlow,
        ClientSetBehindView, ClientSetFixedCamera, ClearProgressMessages,
        ProgressCommand, SetProgressMessage, SetProgressTime,
        GivePawn, ClientGotoState,
		ClientSetActiveRoom, ChatRoomMessage,
		ClientValidate, ClientSetWeaponViewShake,
        ClientSetViewTarget, ClientCapBandwidth,
		ClientOpenMenu, ClientCloseMenu, ClientReplaceMenu, ClientNetworkMessage,
		AdminReply;
    reliable if ( (Role == ROLE_Authority) )
        ClientMessage, TeamMessage, ReceiveLocalizedMessage, QueueAnnouncement;
    unreliable if( Role==ROLE_Authority && !bDemoRecording )
        ClientPlaySound, PlayAnnouncement, PlayRewardAnnouncement, PlayStatusAnnouncement;
    reliable if( Role==ROLE_Authority && !bDemoRecording )
        ClientStopForceFeedback, ClientTravel, ClientSetClassicView;
    unreliable if( Role==ROLE_Authority )
        SetFOVAngle, ClientDamageShake, ClientFlash,ClientUpdateFlagHolder,
        ClientAdjustPosition, ShortClientAdjustPosition, VeryShortClientAdjustPosition, LongClientAdjustPosition;
    unreliable if( (!bDemoRecording || bClientDemoRecording && bClientDemoNetFunc) && Role==ROLE_Authority )
        ClientHearSound;
    reliable if( bClientDemoRecording && ROLE==ROLE_Authority )
		DemoClientSetHUD;

    // Functions client can call.
    unreliable if( Role<ROLE_Authority )
        ServerUpdatePing, ShortServerMove, ServerMove, RocketServerMove;

    reliable if( Role<ROLE_Authority )
		ServerShortTimeout;

    unreliable if( Role<ROLE_Authority )
		ServerVoiceCommand,
        DualServerMove, DualRocketServerMove, TurretServerMove, DualTurretServerMove, DualSpaceFighterServerMove, SpaceFighterServerMove,
        ServerSay, ServerTeamSay, ServerSetHandedness, ServerSetAutotaunt, ServerViewNextPlayer, ServerViewSelf,ServerUse, ServerDrive, ServerToggleBehindView;
    reliable if( Role<ROLE_Authority )
        ServerSpeech, ServerPause, SetPause, ServerMutate, ServerAcknowledgePossession,
        PrevItem, ActivateItem, ServerReStartGame, AskForPawn,
        ChangeName, ChangeVoiceType, ServerChangeTeam, Suicide,
        ServerThrowWeapon, BehindView, Typing,
		ServerValidationResponse, ServerVerifyViewTarget, ServerSpectateSpeed, ServerSetClientDemo,
		ServerSpectate, BecomeSpectator, BecomeActivePlayer;

	// Server Admin replicated functions
	reliable if( Role<ROLE_Authority )
		Admin, AdminCommand, ServerAdminLogin, ServerAdminLoginSilent, AdminLogout, AdminDebug;

	// Voice-chat replicated function
	reliable if (Role < ROLE_Authority)
		ServerSetChatPassword, ServerJoinVoiceChannel, ServerLeaveVoiceChannel, ServerSpeak,
		ServerChangeVoiceChatMode, ServerChatRestriction, ServerRequestBanInfo, ServerGetWeaponStats;

	reliable if ( Role < ROLE_Authority )
		ServerChatDebug;

	reliable if (ROLE==ROLE_Authority)
    	ResetFOV, ClientBecameSpectator, ClientBecameActivePlayer;

// if _RO_
    // Functions server can call.
    reliable if( Role==ROLE_Authority )
    	ClientResetMovement;

    // Spectating stuff, moved here to take advantage of native replication
	reliable if (bNetDirty && bNetOwner && Role == ROLE_Authority)
		bLockedBehindView, bFirstPersonSpectateOnly, bAllowRoamWhileSpectating,
        bViewBlackWhenDead, bViewBlackOnDeadNotViewingPlayers, bAllowRoamWhileDeadSpectating,
		SteamStatsAndAchievements;

	reliable if ( Role < ROLE_Authority )
		ServerInitializeSteamStatInt, ServerInitializeSteamStatFloat, ServerSteamStatsAndAchievementsInitialized;
// end RO

}

native final function SetNetSpeed(int NewSpeed);
native final function string GetPlayerIDHash();
native final function string GetPlayerNetworkAddress();
native final function string GetServerNetworkAddress();
native function string ConsoleCommand( string Command, optional bool bWriteToLog );
native final function LevelInfo GetEntryLevel();
native(544) final function ResetKeyboard();
native final private function ResetInput();

native final function SetViewTarget(Actor NewViewTarget);
native event ClientTravel( string URL, ETravelType TravelType, bool bItems );
native final function string GetURLProtocol();
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();

// Validation.
private native event ClientValidate(string C);
private native event ServerValidationResponse(string R);

native final function bool CheckSpeedHack(float DeltaTime);

/* FindStairRotation()
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);

native event ClientHearSound (
    actor Actor,
    int Id,
    sound S,
    vector SoundLocation,
    vector Parameters,
    bool Attenuate
);

//if _RO_
native final function bool PostFX_IsReady(); //Returns true if postfx system has been inited successfully and is ready to do blur or b&w postfx processing
native final function bool PostFX_IsBloomCapable(); //Returns true if postfx system has inited successfully and is bloom capable
native final function PostFX_SetActive(int Effect, bool Active); //Sets an effect active/inactive
native final function bool PostFX_IsActive(int Effect); //Returns true if an effect is active
native final function PostFX_SetParameter(int Effect, int Param, float Value); //Sets an effect parameter ...
simulated function NotifySpeakingInTeamChannel(); // Gets called by the hud when the player is speaking in the team channel. Put here to avoid casting
native final function string GetServerIP();
native final function bool CharacterAvailable(string CharName);
native final function bool PurchaseCharacter(string CharName); // If this is a purchasable Character, this opens the Steam Store Page for it and returns true(otherwise returns false)

native static final function Advertising_EnterZone(string ZoneName);
native static final function Advertising_ExitZone();

native final function OpenUPNPPorts();

// Steam Workshop
simulated native function EnumerateSubscribedSteamWorkshopFiles();
simulated native function GetSubscribedSteamWorkshopFileDetails(int Index);
simulated native function DownloadSubscribedSteamWorkshopFile(int Index);
//end _RO_

exec function GetWeaponStats()
{
}

function ServerGetWeaponStats(Weapon W)
{
	if ( (Pawn == None) || (Pawn.Weapon == None) )
	{
		log("Weapon stats requested by "$PlayerReplicationInfo.PlayerName$" with pawn "$Pawn$" and no weapon");
		return;
	}
	log("Weapon stats requested by "$PlayerReplicationInfo.PlayerName$" for "$Pawn.Weapon);
	if ( W != None )
		W.StartDebugging();
	Pawn.Weapon.StartDebugging();
}

simulated event PostBeginPlay()
{
	local class<PlayerChatManager> PlayerChatClass;

    super.PostBeginPlay();
	MaxTimeMargin = Level.MaxTimeMargin;
	MaxResponseTime = Default.MaxResponseTime * Level.TimeDilation;

	if ( Level.NetMode == NM_Client )
		SpawnDefaultHUD();

	if (Level.LevelEnterText != "" )
        ClientMessage(Level.LevelEnterText);

    FixFOV();
    SetViewDistance();
    SetViewTarget(self);  // MUST have a view target!
	LastActiveTime = Level.TimeSeconds;

	if ( Level.NetMode == NM_Standalone )
        AddCheats();

    bForcePrecache = (Role < ROLE_Authority);
    ForcePrecacheTime = Level.TimeSeconds + 1.2;

    if ( Level.Game != None )
    	MapHandler = Level.Game.MaplistHandler;

    if ( (PlayerChatType != "") && (Role == ROLE_Authority) )
    {
    	PlayerChatClass = class<PlayerChatManager>(DynamicLoadObject(PlayerChatType, class'Class'));
    	if ( PlayerChatClass != None )
    		ChatManager = Spawn(PlayerChatClass, Self);
    }
}

simulated function bool BeyondViewDistance(vector OtherLocation, float CullDistance)
{
	local float Dist;

	if ( ViewTarget == None )
		return true;

	Dist = VSize(OtherLocation - ViewTarget.Location);

	if ( (CullDistance > 0) && (CullDistance < Dist * FOVBias) )
		return true;

	return ( Region.Zone.bDistanceFog && (Dist > Region.Zone.DistanceFogEnd) );
}

event KickWarning()
{
	ReceiveLocalizedMessage( class'GameMessage', 15 );
}

function ResetTimeMargin()
{
    TimeMargin = -0.1;
	MaxTimeMargin = Level.MaxTimeMargin;
}

function ServerShortTimeout()
{
	local Actor A;

	bShortConnectTimeOut = true;
	ResetTimeMargin();

	// quick update of pickups and gameobjectives since this player is now relevant
	if ( Level.Game.NumPlayers < 8 )
	{
		ForEach AllActors(class'Actor', A)
			if ( (A.NetUpdateFrequency < 1) && !A.bOnlyRelevantToOwner )
				A.NetUpdateTime = FMin(A.NetUpdateTime, Level.TimeSeconds + 0.2 * FRand());
	}
	else
	{
		ForEach AllActors(class'Actor', A)
			if ( (A.NetUpdateFrequency < 1) && !A.bOnlyRelevantToOwner )
				A.NetUpdateTime = FMin(A.NetUpdateTime, Level.TimeSeconds + 0.5 * FRand());
	}
}


function Actor GetPathTo(Actor Dest)
{
	local int i;
	local Actor Best;
	local vector Dir;

	if ( Dest == None )
		return Dest;

	if ( (Pawn.Physics != PHYS_Falling) && ((RouteGoal != Dest) || (Level.TimeSeconds - LastRouteFind > 1+FRand())) )
	{
		MoveTarget = FindPathToward(Dest, false);
		if ( MoveTarget == None )
			return Dest;
	}

	if ( RouteCache[0] == None )
		return Dest;

	if ( RouteCache[1] == None )
		return RouteCache[0];

	Best = RouteCache[0];
	Dir = Normal(RouteCache[1].Location - RouteCache[0].Location);

	// return furthest visible path in a relatively straight line from first
	for ( i=0; i<5; i++ )
	{
		if ( (RouteCache[i] == None) || (VSize(Pawn.Location - RouteCache[i].Location) > 2000) )
			break;
		if ( ((Normal(RouteCache[i].Location - RouteCache[0].Location) Dot Dir) > 0.7)
			&& LineOfSightTo(RouteCache[i]) )
			Best = RouteCache[i];
	}

	return Best;
}

// This is called by the low level audio code whenever a stream is finished
//!! Only for music started with PlayStream()
simulated event StreamFinished(int StreamHandle, Interactions.EStreamFinishReason Reason)
{
	local int i;

	if ( Player != None )
	{
		for ( i = 0; i < Player.LocalInteractions.Length; i++ )
			if ( Player.LocalInteractions[i] != None )
				Player.LocalInteractions[i].StreamFinished( StreamHandle, Reason );
	}
}

exec function KillAll(class<actor> aClass)
{
	local Actor A;
	local Controller C;

	if (Role != ROLE_Authority)
		return;

	//notification
	if (CheatManager != None)
		CheatManager.ReportCheat("KillAll");
	for (C = Level.ControllerList; C != None; C = C.NextController)
		if (PlayerController(C) != None)
			PlayerController(C).ClientMessage("Killed all "$string(aClass));

	if ( ClassIsChildOf(aClass, class'AIController') )
	{
		Level.Game.KillBots(Level.Game.NumBots);
		return;
	}
	if ( ClassIsChildOf(aClass, class'Pawn') )
	{
		KillAllPawns(class<Pawn>(aClass));
		return;
	}
	ForEach DynamicActors(class 'Actor', A)
		if ( ClassIsChildOf(A.class, aClass) )
			A.Destroy();
}

// Kill non-player pawns and their controllers
function KillAllPawns(class<Pawn> aClass)
{
	local Pawn P;

	Level.Game.KillBots(Level.Game.NumBots);
	ForEach DynamicActors(class'Pawn', P)
		if ( ClassIsChildOf(P.Class, aClass)
			&& !P.IsPlayerPawn() )
		{
			if ( P.Controller != None )
				P.Controller.Destroy();
			P.Destroy();
		}
}

exec function ToggleScreenShotMode()
{
	if ( myHUD.bCrosshairShow )
	{
		myHUD.bCrosshairShow = false;
		SetWeaponHand("Hidden");
		myHUD.bHideHUD = true;
		TeamBeaconMaxDist = 0;
		bHideVehicleNoEntryIndicator = true;
	}
	else
	{
		// return to normal
		myHUD.bCrosshairShow = true;
		SetWeaponHand("Right");
		myHUD.bHideHUD = false;
		TeamBeaconMaxDist = default.TeamBeaconMaxDist;
		bHideVehicleNoEntryIndicator = false;
	}
}

exec function SetSpectateSpeed(Float F)
{
	SpectateSpeed = F;
	ServerSpectateSpeed(F);
}

function ClientSetWeaponViewShake(Bool B)
{
	bWeaponViewShake = B;
}

function ClientSetClassicView()
{
	Level.bClassicView = true;
}

function ServerSpectateSpeed(Float F)
{
	SpectateSpeed = F;
}

function ServerGivePawn()
{
	GivePawn(Pawn);
}

function ClientCapBandwidth(int Cap)
{
	ClientCap = Cap;
	if ( (Player != None) && (Player.CurrentNetSpeed > Cap) )
		SetNetSpeed(Cap);
}

function PendingStasis()
{
    bStasis = true;
    Pawn = None;
    GotoState('Scripting');
}

function AddCheats()
{
// if _RO_
	// Overriden so we can do this during Dev. Maybe remove before shipping
//	if ( CheatManager == None && (Level.NetMode == NM_Standalone ||
//        Level.NetMode != NM_Client) )
//		CheatManager = new(self) CheatClass;
// else
	// Assuming that this never gets called for NM_Client
	if ( CheatManager == None && (Level.NetMode == NM_Standalone) )
		CheatManager = new(self) CheatClass;
// endif _RO_
}

function MakeAdmin()
{
	if ( AdminManager == None && Level != None && Level.Game != None && Level.Game.AccessControl != None)
	{
		if (Level.Game.AccessControl.AdminClass == None)
			Log("AdminClass is None");
		else
			AdminManager = new(self) Level.Game.AccessControl.AdminClass;
	}
}

function HandlePickup(Pickup pick)
{
	ReceiveLocalizedMessage(pick.MessageClass,,,,pick.class);
}

event ClientSetViewTarget( Actor a )
{
	local bool bNewViewTarget;

	if ( A == None )
	{
		if ( ViewTarget != self )
			SetLocation(CalcViewLocation);
		ServerVerifyViewTarget();
	}
	else
	{
		bNewViewTarget = (ViewTarget != a);
		SetViewTarget( a );
		if (bNewViewTarget)
			a.POVChanged(self, false);
	}
}

function ServerVerifyViewTarget()
{
	if ( ViewTarget == self )
		return;
	if ( ViewTarget == None )
		return;

	ClientSetViewTarget(ViewTarget);
}

/* SpawnDefaultHUD()
Spawn a HUD (make sure that PlayerController always has valid HUD, even if \
ClientSetHUD() hasn't been called\
*/
function SpawnDefaultHUD()
{
    myHUD = spawn(class'HUD',self);
}

/* Reset()
reset actor to initial state - used when restarting level without reloading.
*/
function Reset()
{
	local vehicle DrivenVehicle;

    DrivenVehicle = Vehicle(Pawn);
	if( DrivenVehicle != None )
		DrivenVehicle.KDriverLeave(true); // Force the driver out of the car

	if ( Pawn != None )
		PawnDied( Pawn );
    super.Reset();
    SetViewTarget( Self );
    ClientSetViewTarget( self );
	ClientSetFixedCamera( false );
    bBehindView = false;
    WaitDelay	= Level.TimeSeconds + 2;
    SetViewDistance();
    FixFOV();
    if ( PlayerReplicationInfo.bOnlySpectator )
    	GotoState('Spectating');
    else
	GotoState('PlayerWaiting');
}

event ClientReset()
{
	bBehindView		= false;
	bFixedCamera	= false;
	SetViewTarget( self );
	SetViewDistance();

    if ( PlayerReplicationInfo.bOnlySpectator )
    	GotoState('Spectating');
    else
	GotoState('PlayerWaiting');
}

function CleanOutSavedMoves()
{
    local SavedMove Next;

	// clean out saved moves
	while ( SavedMoves != None )
	{
		Next = SavedMoves.NextMove;
		SavedMoves.Destroy();
		SavedMoves = Next;
	}
	if ( PendingMove != None )
	{
		PendingMove.Destroy();
		PendingMove = None;
	}
}

/* InitInputSystem()
Spawn the appropriate class of PlayerInput
Only called for playercontrollers that belong to local players
*/
event InitInputSystem()
{
	PlayerInput = new(self) InputClass;
}

/* ClientGotoState()
server uses this to force client into NewState
*/
function ClientGotoState(name NewState, name NewLabel)
{
    GotoState(NewState,NewLabel);
}

function AskForPawn()
{
	if ( IsInState('GameEnded') )
		ClientGotoState('GameEnded', 'Begin');
	else if ( IsInState('RoundEnded') )
		ClientGotoState('RoundEnded', 'Begin');
	else if ( Pawn != None )
		GivePawn(Pawn);
	else
	{
		bFrozen = false;
		ServerRestartPlayer();
	}
}

function GivePawn(Pawn NewPawn)
{
    if ( NewPawn == None )
        return;
    Pawn = NewPawn;
    NewPawn.Controller = self;
    ClientRestart(Pawn);
}

/* GetFacingDirection()
returns direction faced relative to movement dir
0 = forward
16384 = right
32768 = back
49152 = left
*/
function int GetFacingDirection()
{
    local vector X,Y,Z, Dir;

    GetAxes(Pawn.Rotation, X,Y,Z);
    Dir = Normal(Pawn.Acceleration);
    if ( Y Dot Dir > 0 )
        return ( 49152 + 16384 * (X Dot Dir) );
    else
        return ( 16384 - 16384 * (X Dot Dir) );
}

// Possess a pawn
function Possess(Pawn aPawn)
{
    if ( PlayerReplicationInfo.bOnlySpectator )
        return;

	ResetFOV();
    aPawn.PossessedBy(self);
    Pawn = aPawn;
    Pawn.bStasis = false;
	ResetTimeMargin();
    CleanOutSavedMoves();  // don't replay moves previous to possession
	if ( Vehicle(Pawn) != None && Vehicle(Pawn).Driver != None )
		PlayerReplicationInfo.bIsFemale = Vehicle(Pawn).Driver.bIsFemale;
	else
    PlayerReplicationInfo.bIsFemale = Pawn.bIsFemale;
    ServerSetHandedness(Handedness);
    ServerSetAutoTaunt(bAutoTaunt);
    Restart();
}

function AcknowledgePossession(Pawn P)
{
	if ( Viewport(Player) != None )
	{
		AcknowledgedPawn = P;
		if ( P != None )
			P.SetBaseEyeHeight();
		ServerAcknowledgePossession(P, Handedness, bAutoTaunt);
	}
}


function ServerAcknowledgePossession(Pawn P, float NewHand, bool bNewAutoTaunt)
{
	ResetTimeMargin();
    AcknowledgedPawn = P;
    ServerSetHandedness(NewHand);
    ServerSetAutoTaunt(bNewAutoTaunt);
}

// unpossessed a pawn (not because pawn was killed)
function UnPossess()
{
    if ( Pawn != None )
    {
        SetLocation(Pawn.Location);
        Pawn.RemoteRole = ROLE_SimulatedProxy;
        Pawn.UnPossessed();
		CleanOutSavedMoves();  // don't replay moves previous to unpossession
        if ( Viewtarget == Pawn )
            SetViewTarget(self);
    }
    Pawn = None;
    GotoState('Spectating');
}

function ViewNextBot()
{
	if ( CheatManager != None )
		CheatManager.ViewBot();
}

// unpossessed a pawn (because pawn was killed)
function PawnDied(Pawn P)
{
	if ( P != Pawn )
		return;
    EndZoom();
    if ( Pawn != None )
        Pawn.RemoteRole = ROLE_SimulatedProxy;
    if ( ViewTarget == Pawn )
        bBehindView = true;

    Super.PawnDied(P);
}

simulated function ClientUpdateFlagHolder(PlayerReplicationInfo PRI, int i)
{
	if ( (Role == ROLE_Authority) || (GameReplicationInfo == None) )
		return;
	GameReplicationInfo.FlagHolder[i] = PRI;
}

simulated function ClientSetHUD(class<HUD> newHUDClass, class<Scoreboard> newScoringClass )
{
    if ( myHUD != None )
        myHUD.Destroy();

    if (newHUDClass == None)
        myHUD = None;
    else
    {
        myHUD = spawn (newHUDClass, self);

        if (myHUD == None)
            log ("PlayerController::ClientSetHUD(): Could not spawn a HUD of class "$newHUDClass, 'Error');
        else
            myHUD.SetScoreBoardClass( newScoringClass );
    }

    if( Level.Song != "" && Level.Song != "None" )
		ClientSetInitialMusic( Level.Song, MTRAN_Fade );
}

// jdf ---
// Server ignores this call, client plays effect
simulated function ClientPlayForceFeedback( String EffectName )
{
    if (bForceFeedbackSupported && Viewport(Player) != None)
        PlayFeedbackEffect( EffectName );
}

simulated function StopForceFeedback( optional String EffectName )
{
    if (bForceFeedbackSupported && Viewport(Player) != None)
		StopFeedbackEffect( EffectName );
}

function ClientStopForceFeedback( optional String EffectName )
{
    if (bForceFeedbackSupported && Viewport(Player) != None)
		StopFeedbackEffect( EffectName );
}
// --- jdf

final function float UpdateFlashComponent(float current, float Step, float goal)
{
	if ( goal > current )
		return FMin(current + Step, goal);
	else
		return FMax(current - Step, goal);
}

function ViewFlash(float DeltaTime)
{
    local vector goalFog;
    local float goalscale, delta, Step;
    local PhysicsVolume ViewVolume;

    delta = FMin(0.1, DeltaTime);
    goalScale = 1; // + ConstantGlowScale;
    goalFog = vect(0,0,0); // ConstantGlowFog;

    if ( Pawn != None )
    {
		if ( bBehindView )
			ViewVolume = Level.GetPhysicsVolume(CalcViewLocation);
		else
			ViewVolume = Pawn.HeadVolume;

		goalScale += ViewVolume.ViewFlash.X;
		goalFog += ViewVolume.ViewFog;
    }
	Step = 0.6 * delta * ScreenFlashScaling;
	FlashScale.X = UpdateFlashComponent(FlashScale.X,step,goalScale);
    FlashScale = FlashScale.X * vect(1,1,1);

	FlashFog.X = UpdateFlashComponent(FlashFog.X,step,goalFog.X);
	FlashFog.Y = UpdateFlashComponent(FlashFog.Y,step,goalFog.Y);
	FlashFog.Z = UpdateFlashComponent(FlashFog.Z,step,goalFog.Z);
}

simulated 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 ( Level.NetMode == NM_DedicatedServer || GameReplicationInfo == None )
		return;

    Message.Static.ClientReceive( Self, Switch, RelatedPRI_1, RelatedPRI_2, OptionalObject );
	if ( Message.static.IsConsoleMessage(Switch) && (Player != None) && (Player.Console != None) )
		Player.Console.Message(Message.Static.GetString(Switch, RelatedPRI_1, RelatedPRI_2, OptionalObject),0 );
}

simulated event ChatRoomMessage( byte Result, int ChannelIndex, optional PlayerReplicationInfo RelatedPRI )
{
	local VoiceChatRoom VCR;
	local string str;

	if ( VoiceReplicationInfo != None && ChatRoomMessageClass != None )
	{
		VCR = VoiceReplicationInfo.GetChannelAt(ChannelIndex);
		if ( VCR != None )
			str = VCR.GetTitle();

		if ( (str ~= "Team") || (str ~= "Public") || (str ~= "Local") )
			return;
		ClientMessage( ChatRoomMessageClass.static.AssembleMessage(Result, str, RelatedPRI) );
	}
}

event ClientMessage( coerce string S, optional Name Type )
{
	// Wait for player to be up to date with replication when joining a server, before stacking up messages
	if ( Level.NetMode == NM_DedicatedServer || GameReplicationInfo == None )
		return;

    if (Type == '')
        Type = 'Event';

	TeamMessage(PlayerReplicationInfo, S, Type);
}

function bool AllowTextToSpeech(PlayerReplicationInfo PRI, name Type)
{
	if ( bNoTextToSpeechVoiceMessages || (PRI == None) )
		return false;

    if ( Type == 'Say' )
	{
	    //if _RO_
	    if ( PRI.bSilentAdmin )
	        return true;
	    //end _RO_
		if ( PRI.bAdmin || ((GameReplicationInfo != None) && !GameReplicationInfo.bTeamGame) || (PRI == PlayerReplicationInfo) )
			return true;
		if ( IsInState('GameEnded') || IsInState('RoundEnded') )
			return true;
		return !bOnlySpeakTeamText;
	}
    else if ( Type == 'TeamSay' )
		return true;
	return false;
}

event TeamMessage( PlayerReplicationInfo PRI, coerce string S, name Type  )
{
	local string c;

	// Wait for player to be up to date with replication when joining a server, before stacking up messages
	if ( Level.NetMode == NM_DedicatedServer || GameReplicationInfo == None )
		return;

	if( AllowTextToSpeech(PRI, Type) )
		TextToSpeech( S, TextToSpeechVoiceVolume );
	if ( Type == 'TeamSayQuiet' )
		Type = 'TeamSay';

	if ( myHUD != None )
		myHUD.Message( PRI, c$S, Type );

	if ( (Player != None) && (Player.Console != None) )
	{
		if ( PRI!=None )
		{
			if ( PRI.Team!=None && GameReplicationInfo.bTeamGame)
			{
    			if (PRI.Team.TeamIndex==0)
					c = chr(27)$chr(200)$chr(1)$chr(1);
    			else if (PRI.Team.TeamIndex==1)
        			c = chr(27)$chr(125)$chr(200)$chr(253);
			}
			S = PRI.PlayerName$": "$S;
		}
		Player.Console.Chat( c$s, 6.0, PRI );
	}
}

simulated function PlayBeepSound();

simulated function PrecacheAnnouncements()
{
	if ( RewardAnnouncer != None )
		RewardAnnouncer.PrecacheAnnouncements(true);
	if ( StatusAnnouncer != None )
		StatusAnnouncer.PrecacheAnnouncements(false);
}

simulated function PlayStatusAnnouncement(name AName, byte AnnouncementLevel, optional bool bForce)
{
	local float Atten;
	local sound ASound;

	// Wait for player to be up to date with replication when joining a server, before stacking up messages
	if ( Level.NetMode == NM_DedicatedServer || GameReplicationInfo == None )
		return;

	if ( (AnnouncementLevel > AnnouncerLevel) || (StatusAnnouncer == None) )
		return;
	if ( !bForce && (Level.TimeSeconds - LastPlaySound < 1) )
		return;
    LastPlaySound = Level.TimeSeconds;  // so voice messages won't overlap
	LastPlaySpeech = Level.TimeSeconds;	// don't want chatter to overlap announcements

	Atten = 2.0 * FClamp(0.1 + float(AnnouncerVolume)*0.225,0.2,1.0);
	ASound = StatusAnnouncer.GetSound(AName);
	if ( ASound != None )
		ClientPlaySound(ASound,true,Atten,SLOT_Talk);
}

simulated function PlayRewardAnnouncement(name AName, byte AnnouncementLevel, optional bool bForce)
{
	local float Atten;
	local sound ASound;

	// Wait for player to be up to date with replication when joining a server, before stacking up messages
	if ( Level.NetMode == NM_DedicatedServer || GameReplicationInfo == None )
		return;

	if ( (AnnouncementLevel > AnnouncerLevel) || (RewardAnnouncer == None) )
		return;
	if ( !bForce && (Level.TimeSeconds - LastPlaySound < 1) )
		return;
    LastPlaySound = Level.TimeSeconds;  // so voice messages won't overlap
	LastPlaySpeech = Level.TimeSeconds;	// don't want chatter to overlap announcements

	Atten = 2.0 * FClamp(0.1 + float(AnnouncerVolume)*0.225,0.2,1.0);
	ASound = RewardAnnouncer.GetSound(AName);
	if ( ASound != None )
		ClientPlaySound(ASound,true,Atten,SLOT_Talk);
}

// PlayAnnouncement is OBSOLETE
simulated function PlayAnnouncement(sound ASound, byte AnnouncementLevel, optional bool bForce)
{
	local float Atten;

	if ( AnnouncementLevel > AnnouncerLevel )
		return;
	if ( !bForce && (Level.TimeSeconds - LastPlaySound < 1) )
		return;
    LastPlaySound = Level.TimeSeconds;  // so voice messages won't overlap
	LastPlaySpeech = Level.TimeSeconds;	// don't want chatter to overlap announcements

	Atten = 2.0 * FClamp(0.1 + float(AnnouncerVolume)*0.225,0.2,1.0);
	ClientPlaySound(ASound,true,Atten,SLOT_Talk);
}

// CustomizeAnnouncer is obsolete
function Sound CustomizeAnnouncer(Sound AnnouncementSound)
{
	return AnnouncementSound;
}

simulated function QueueAnnouncement( name ASoundName, byte AnnouncementLevel,
									 optional AnnouncerQueueManager.EAPriority Priority, optional byte Switch )
{
	if ( AnnouncementLevel > AnnouncerLevel || Level.NetMode == NM_DedicatedServer || GameReplicationInfo == None )
		return;

	if ( AnnouncerQueueManager == None )
	{
		AnnouncerQueueManager = Spawn(class'AnnouncerQueueManager');
		AnnouncerQueueManager.InitFor( Self );
	}

	if ( AnnouncerQueueManager != None )
		AnnouncerQueueManager.AddItemToQueue( ASoundName, Priority, Switch );
}

function bool AllowVoiceMessage(name MessageType)
{
	if ( Level.NetMode == NM_Standalone )
		return true;

	if ( Level.TimeSeconds - OldMessageTime < 3 )
	{
		if ( (MessageType == 'TAUNT') || (MessageType == 'AUTOTAUNT') )
			return false;
		if ( Level.TimeSeconds - OldMessageTime < 1 )
			return false;
	}
	if ( Level.TimeSeconds - OldMessageTime < 6 )
		OldMessageTime = Level.TimeSeconds + 3;
	else
		OldMessageTime = Level.TimeSeconds;
	return true;
}

//Play a sound client side (so only client will hear it
simulated function ClientPlaySound(sound ASound, optional bool bVolumeControl, optional float inAtten, optional ESoundSlot slot )
{
    local float atten;

    atten = 1.0;
    if( bVolumeControl )
        atten = FClamp(inAtten,0,2);

	if ( ViewTarget != None )
		ViewTarget.PlaySound(ASound, slot, atten,,,,false);
}

simulated function ClientReliablePlaySound(sound ASound, optional bool bVolumeControl )
{
    ClientPlaySound(ASound, bVolumeControl);
}

simulated event Destroyed()
{
    local SavedMove Next;
    local Vehicle DrivenVehicle;
	local Pawn Driver;

	// cheatmanager, adminmanager, and playerinput cleaned up in C++ PostScriptDestroyed()

	if ( AdminManager != None )
		AdminManager.DoLogout();

    StopFeedbackEffect();

    if ( Pawn != None )
    {
		// If its a vehicle, just destroy the driver, otherwise do the normal.
        DrivenVehicle = Vehicle(Pawn);
		if( DrivenVehicle != None )
		{
			Driver = DrivenVehicle.Driver;
			DrivenVehicle.KDriverLeave(true); // Force the driver out of the car
			if ( Driver != None )
			{
				Driver.Health = 0;
				Driver.Died( self, class'Suicided', Driver.Location );
			}
		}
		else
		{
			Pawn.Health = 0;
			Pawn.Died( self, class'Suicided', Pawn.Location );
        }
    }
    if ( myHUD != None )
		myHud.Destroy();
	if ( AnnouncerQueueManager != None )
		AnnouncerQueueManager.Destroy();

    while ( FreeMoves != None )
    {
        Next = FreeMoves.NextMove;
        FreeMoves.Destroy();
        FreeMoves = Next;
    }
    while ( SavedMoves != None )
    {
        Next = SavedMoves.NextMove;
        SavedMoves.Destroy();
        SavedMoves = Next;
    }

    if( PlayerSecurity != None )
    {
        PlayerSecurity.Destroy();
        PlayerSecurity = None;
    }

	if ( ChatManager != None )
		ChatManager.Destroy();

    super.Destroyed();
}

function ClientSetMusic( string NewSong, EMusicTransition NewTransition )
{
	local float FadeIn, FadeOut;

	switch (NewTransition)
	{
	case MTRAN_Segue:
		FadeIn = 7.0;
		FadeOut = 3.0;
		break;

	case MTRAN_Fade:
		FadeIn = 3.0;
		FadeOut = 3.0;
		break;

	case MTRAN_FastFade:
		FadeIn = 1.0;
		FadeOut = 1.0;
		break;

	case MTRAN_SlowFade:
		FadeIn = 5.0;
		FadeOut = 5.0;
		break;
	}

    StopAllMusic( FadeOut );

    if ( NewSong != "" )
	    PlayMusic( NewSong, FadeIn );

    Song        = NewSong;
    Transition  = NewTransition;
    if (Player!=None && Player.Console!=None)
    	Player.Console.SetMusic(NewSong);
}

function ClientSetInitialMusic( string NewSong, EMusicTransition NewTransition )
{
	local string SongName;

	if ( Song != "" )
		return;

    SongName = NewSong;
    if (Player!=None && Player.Console!=None)
    	SongName = Player.Console.SetInitialMusic(NewSong);

    ClientSetMusic(SongName,NewTransition);
}

// ------------------------------------------------------------------------
// Zooming/FOV change functions

function ToggleZoomWithMax(float MaxZoomLevel)
{
    if ( DefaultFOV != DesiredFOV )
        EndZoom();
    else
		StartZoomWithMax(MaxZoomLevel);
}

function StartZoomWithMax(float MaxZoomLevel)
{
	DesiredZoomLevel = MaxZoomLevel;
	myHUD.FadeZoom();
    ZoomLevel = 0.0;
    bZooming = true;
}

function ToggleZoom()
{
	ToggleZoomWithMax(0.9);
}

function StartZoom()
{
	StartZoomWithMax(0.9);
}

function StopZoom()
{
    bZooming = false;
}

function EndZoom()
{
	if ( DesiredFOV != DefaultFOV )
		myHUD.FadeZoom();
    bZooming = false;
    DesiredFOV = DefaultFOV;
}

simulated function FixFOV()
{
	FOVAngle = Default.DefaultFOV;
    DesiredFOV = Default.DefaultFOV;
    DefaultFOV = Default.DefaultFOV;
}

// if _RO_
// Sets the far clipping view distance based on the level settings
simulated function SetViewDistance()
{
	switch (Level.ViewDistanceLevel)
	{
    	case VDL_Default_1000m:
            ConsoleCommand("FARCLIP 65536");
    		break;

    	case VDL_Medium_2000m:
            ConsoleCommand("FARCLIP 131072");
    		break;

    	case VDL_High_3000m:
            ConsoleCommand("FARCLIP 196608");
    		break;

    	case VDL_Extreme_4000m:
            ConsoleCommand("FARCLIP 262144");
    		break;

    	default:
            ConsoleCommand("FARCLIP 65536");
	}
}
// end _RO_

function SetFOV(float NewFOV)
{
    DesiredFOV = NewFOV;
    FOVAngle = NewFOV;
}

function ResetFOV()
{
    DesiredFOV = DefaultFOV;
	FOVAngle = DefaultFOV;
}

exec function FOV(float F)
{
    if( (F >= 80.0) || (Level.Netmode==NM_Standalone) || PlayerReplicationInfo.bOnlySpectator )
    {
        DefaultFOV = FClamp(F, 1, 170);
        DesiredFOV = DefaultFOV;
        SaveConfig();
    }
}

exec function Mutate(string MutateString)
{
	ServerMutate(MutateString);
}

function ServerMutate(string MutateString)
{
	if( Level.NetMode == NM_Client )
		return;
	Level.Game.BaseMutator.Mutate(MutateString, Self);
}

exec function SetSensitivity(float F)
{
    PlayerInput.UpdateSensitivity(F);
}

exec function SetMouseSmoothing( int Mode )
{
    PlayerInput.UpdateSmoothing( Mode );
}

exec function SetMouseAccel(float F)
{
	PlayerInput.UpdateAccel(F);
}

exec function ForceReload()
{
    if ( (Pawn != None) && (Pawn.Weapon != None) )
    {
        //Pawn.Weapon.ForceReload(); //merge_hack
    }
}

// ------------------------------------------------------------------------
// Messaging functions

function bool AllowTextMessage(string Msg)
{
	local int i;

    //if _RO_
	if ( PlayerReplicationInfo.bSilentAdmin )
		return true;
    //end _RO_
	if ( (Level.NetMode == NM_Standalone) || PlayerReplicationInfo.bAdmin )
		return true;
	if ( ( Level.Pauser == none) && (Level.TimeSeconds - LastBroadcastTime < 2 ) )
		return false;

	// lower frequency if same text
	if ( Level.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 = Level.TimeSeconds;
	return true;
}

// Send a message to all players.
exec function Say( string Msg )
{
	Msg = Left(Msg,128);

	if ( AllowTextMessage(Msg) )
		ServerSay(Msg);
}

function ServerSay( string Msg )
{
	local controller C;

	Msg = Level.Game.StripColor(Msg);

	// center print admin messages which start with #
	//if _RO_
	if ( (PlayerReplicationInfo.bAdmin || PlayerReplicationInfo.bSilentAdmin) && left(Msg,1) == "#" )
	//else
	//if (PlayerReplicationInfo.bAdmin && left(Msg,1) == "#" )
	//end _RO_
	{
		Msg = right(Msg,len(Msg)-1);
		for( C=Level.ControllerList; C!=None; C=C.nextController )
			if( C.IsA('PlayerController') )
			{
				PlayerController(C).ClearProgressMessages();
				PlayerController(C).SetProgressTime(6);
				PlayerController(C).SetProgressMessage(0, Msg, class'Canvas'.Static.MakeColor(255,255,255));
			}
		return;
	}

	Level.Game.Broadcast(self, Msg, 'Say');
}

exec function TeamSay( string Msg )
{
	Msg = Left(Msg,128);
	if ( AllowTextMessage(Msg) )
		ServerTeamSay(Msg);
}

function ServerTeamSay( string Msg )
{
	LastActiveTime = Level.TimeSeconds;

	Msg = Level.Game.StripColor(Msg);

	if( !GameReplicationInfo.bTeamGame )
	{
		Say( Msg );
		return;
	}

    Level.Game.BroadcastTeam( self, Level.Game.ParseMessageString( Level.Game.BaseMutator , self, Msg ) , 'TeamSay');
}

// ------------------------------------------------------------------------

function ServerSetAutoTaunt(bool Value)
{
	bAutoTaunt = Value;
}

exec function SetAutoTaunt(bool Value)
{
	class'PlayerController'.default.bAutoTaunt = Value;
	class'PlayerController'.static.StaticSaveConfig();
	bAutoTaunt = Value;

	ServerSetAutoTaunt(Value);
}

// ------------------------------------------------------------------------

function ServerSetHandedness( float hand)
{
    Handedness = hand;
    if ( (Pawn != None) && (Pawn.Weapon != None) )
        Pawn.Weapon.SetHand(Handedness);
}

function SetHand(int IntValue)
{
    Handedness = IntValue;
    SaveConfig();
    if( (Pawn != None) && (Pawn.Weapon != None) )
        Pawn.Weapon.SetHand(Handedness);

    ServerSetHandedness(Handedness);
}

exec function SetWeaponHand ( string S )
{
    if ( S ~= "Left" )
        Handedness = -1;
    else if ( S~= "Right" )
        Handedness = 1;
    else if ( S ~= "Center" )
        Handedness = 0;
    else if ( S ~= "Hidden" )
        Handedness = 2;
    SetHand(Handedness);
}

function bool IsDead()
{
	return false;
}

exec function ShowGun ()
{
    if( Handedness == 2 )
        Handedness = 1;
    else
        Handedness = 2;

    SetHand(Handedness);
}

event PreClientTravel()
{
    log("PreClientTravel");
    ClientStopForceFeedback();  // jdf
}

function ClientSetFixedCamera(bool B)
{
    bFixedCamera = B;
}

function ClientSetBehindView(bool B)
{
    local bool bWasBehindView;

    bWasBehindView = bBehindView;
    bBehindView = B;
    CameraDist = Default.CameraDist;
    if (bBehindView != bWasBehindView)
	    ViewTarget.POVChanged(self, true);

    if (Vehicle(Pawn) != None)
    {
    	Vehicle(Pawn).bDesiredBehindView = B;
    	Pawn.SaveConfig();
    }
}

// if _RO_
function ClientVoiceMessage(PlayerReplicationInfo Sender, PlayerReplicationInfo Recipient, name messagetype, byte messageID, optional Pawn soundSender, optional vector senderLocation)
// else
// function ClientVoiceMessage(PlayerReplicationInfo Sender, PlayerReplicationInfo Recipient, name messagetype, byte messageID)
// end if _RO_
{
    local VoicePack V;

    if ( (Sender == None) || (Sender.voicetype == None) || (Player.Console == None) )
        return;

    V = Spawn(Sender.voicetype, self);
    if ( V != None )
        V.ClientInitialize(Sender, Recipient, messagetype, messageID);
}

/* ForceDeathUpdate()
Make sure ClientAdjustPosition immediately informs client of pawn's death
*/
function ForceDeathUpdate()
{
    LastUpdateTime = Level.TimeSeconds - 10;
}

/* RocketServerMove()
compressed version of server move for PlayerRocketing state
*/
// Modified to handle Red Orchestra movements
function RocketServerMove
(
	float	TimeStamp,
	vector	InAccel,
	vector	ClientLoc,
	bool	NewbDuck,
	byte	ClientRoll,
	int		View
)
{
	local byte NewActions;

	// Put together new actions to send (compressed into one byte)
	if (NewbDuck)
		NewActions += 2;

	ServerMove(TimeStamp,InAccel,ClientLoc,NewActions, DCLICK_NONE,ClientRoll,View);
}

/* DualRocketServerMove()
compressed version of server move for PlayerRocketing state
*/
function DualRocketServerMove
(
	float	TimeStamp0,
	vector	InAccel0,
	bool	NewbDuck0,
	byte	ClientRoll0,
	int		View0,
	float	TimeStamp,
	vector	InAccel,
	vector	ClientLoc,
	bool	NewbDuck,
	byte	ClientRoll,
	int		View
)
{
	local byte NewActions, NewActions0;

	// Put together new actions to send (compressed into one byte)
	if (NewbDuck)
		NewActions += 2;

	if (NewbDuck0)
		NewActions0 += 2;

	ServerMove(TimeStamp0,InAccel0,vect(0,0,0),NewActions0, DCLICK_NONE,ClientRoll0,View0);
	ServerMove(TimeStamp,InAccel,ClientLoc,NewActions, DCLICK_NONE,ClientRoll,View);
}

function SpaceFighterServerMove
(
	float	TimeStamp,
	vector	InAccel,
	vector	ClientLoc,
	bool	NewbDuck,
	int     ViewPitch,
	int		ViewYaw,
	int		ViewRoll
)
{
	local	Rotator		BackupView;
	local 	byte 		NewActions;

	// Put together new actions to send (compressed into one byte)
	if (NewbDuck)
		NewActions += 2;

	BackupView.Pitch	= ViewPitch;
	BackupView.Yaw		= ViewYaw;
	BackupView.Roll		= ViewRoll;
	SetRotation( BackupView );
	if ( Pawn != None )
		Pawn.SetRotation( BackupView );

	Global.ServerMove(TimeStamp,InAccel,ClientLoc,NewActions, DCLICK_NONE,0,0);
}

function DualSpaceFighterServerMove
(
	float	TimeStamp0,
	vector	InAccel0,
	bool	NewbDuck0,
	int     ViewPitch0,
	int		ViewYaw0,
	int		ViewRoll0,
	float	TimeStamp,
	vector	InAccel,
	vector	ClientLoc,
	bool	NewbDuck,
	int     ViewPitch,
	int		ViewYaw,
	int		ViewRoll
)
{
	local	Rotator		BackupView;
	local 	byte 		NewActions, NewActions0;

	// Put together new actions to send (compressed into one byte)
	if (NewbDuck)
		NewActions += 2;

	if (NewbDuck0)
		NewActions0 += 2;

	BackupView.Pitch	= ViewPitch0;
	BackupView.Yaw		= ViewYaw0;
	BackupView.Roll		= ViewRoll0;
	SetRotation( BackupView );
	if ( Pawn != None )
		Pawn.SetRotation( BackupView );

	Global.ServerMove(TimeStamp0,InAccel0,vect(0,0,0),NewActions0, DCLICK_NONE,0,0);

	BackupView.Pitch	= ViewPitch;
	BackupView.Yaw		= ViewYaw;
	BackupView.Roll		= ViewRoll;
	SetRotation( BackupView );
	if ( Pawn != None )
		Pawn.SetRotation( BackupView );

	Global.ServerMove(TimeStamp,InAccel,ClientLoc,NewActions, DCLICK_NONE,0,0);
}

/* TurretServerMove()
compressed version of server move for PlayerTurreting state
*/
function TurretServerMove
(
	float	TimeStamp,
	vector	ClientLoc,
	bool	NewbDuck,
	byte	ClientRoll,
	int		View,
	optional int FreeAimRot
)
{
	local byte NewActions;

	// Put together new actions to send (compressed into one byte)
	if (NewbDuck)
		NewActions += 2;

	ServerMove(TimeStamp,Vect(0,0,0),ClientLoc,NewActions, DCLICK_NONE,ClientRoll,View,FreeAimRot);
}

/* DualTurretServerMove()
compressed version of server move for PlayerTurreting state
*/
function DualTurretServerMove
(
	float	TimeStamp0,
	bool	NewbDuck0,
	byte	ClientRoll0,
	int		View0,
	float	TimeStamp,
	vector	ClientLoc,
	bool	NewbDuck,
	byte	ClientRoll,
	int		View,
	optional int FreeAimRot
)
{
	local byte NewActions, NewActions0;

	// Put together new actions to send (compressed into one byte)
	if (NewbDuck)
		NewActions += 2;

	if (NewbDuck0)
		NewActions0 += 2;

	ServerMove(TimeStamp0,Vect(0,0,0),vect(0,0,0),NewActions0, DCLICK_NONE,ClientRoll0,View0,FreeAimRot);
	ServerMove(TimeStamp,Vect(0,0,0),ClientLoc,NewActions, DCLICK_NONE,ClientRoll,View,FreeAimRot);
}

/* ShortServerMove()
compressed version of server move for bandwidth saving
*/
// Converted to handle Red Orchestra movement
function ShortServerMove
(
    float TimeStamp,
    vector ClientLoc,
	byte NewActions,
    byte ClientRoll,
    int View,
    optional int FreeAimRot
)
{
    ServerMove(TimeStamp,vect(0,0,0),ClientLoc,NewActions,DCLICK_None,ClientRoll,View,FreeAimRot);
}

/* DualServerMove()
- replicated function sent by client to server - contains client movement and firing info for two moves
*/
// Modified to handle Red Orchestra movement
function DualServerMove
(
	float TimeStamp0,
	vector InAccel0,
	byte NewActions0,
	eDoubleClickDir DoubleClickMove0,
	int View0,
    float TimeStamp,
    vector InAccel,
    vector ClientLoc,
	byte NewActions,
    eDoubleClickDir DoubleClickMove,
    byte ClientRoll,
    int View,
    optional int FreeAimRot,
    optional byte OldTimeDelta,
    optional int OldAccel
)
{
    ServerMove(TimeStamp0,InAccel0,vect(0,0,0),NewActions0,DoubleClickMove0,
			ClientRoll,View0,FreeAimRot);
	if ( ClientLoc == vect(0,0,0) )
		ClientLoc = vect(0.1,0,0);
    ServerMove(TimeStamp,InAccel,ClientLoc,NewActions,DoubleClickMove,ClientRoll,View,FreeAimRot,OldTimeDelta,OldAccel);
}

/* ServerMove()
- replicated function sent by client to server - contains client movement and firing info.
*/
// Modified to support Red Orchestra's movement needs
function ServerMove
(
    float TimeStamp,
    vector InAccel,
    vector ClientLoc,
	byte NewActions,
    eDoubleClickDir DoubleClickMove,
    byte ClientRoll,
    int View,
    optional int FreeAimRot,
    optional byte OldTimeDelta,
    optional int OldAccel
)
{
    local float DeltaTime, clientErr, OldTimeStamp;
    local rotator DeltaRot, Rot, ViewRot;
    local vector Accel, LocDiff;
    local int maxPitch, ViewPitch, ViewYaw;
    local bool NewbPressedJump, OldbRun, OldbSprint;
    local eDoubleClickDir OldDoubleClickMove;
	local bool NewbRun, NewbDuck, NewbJumpStatus, NewbSprint, NewbCrawl;
	local int FreeAimPitch, FreeAimYaw;
	local rotator NewFreeAimRot;

    // If this move is outdated, discard it.
    if ( CurrentTimeStamp >= TimeStamp )
        return;

	if ( AcknowledgedPawn != Pawn )
	{
		OldTimeDelta = 0;
		InAccel = vect(0,0,0);
		GivePawn(Pawn);
	}

	// Decode bit mask
	NewbRun = (NewActions & 1) != 0;
	NewbDuck = (NewActions & 2) != 0;
	NewbJumpStatus = (NewActions & 4) != 0;
	NewbSprint = (NewActions & 8) != 0;
	NewbCrawl = (NewActions & 16) != 0;

    // if OldTimeDelta corresponds to a lost packet, process it first
    if (  OldTimeDelta != 0 )
    {
        OldTimeStamp = TimeStamp - float(OldTimeDelta)/500 - 0.001;
        if ( CurrentTimeStamp < OldTimeStamp - 0.001 )
        {
            // split out components of lost move (approx)
			// Accel is encoded using 8 bits per component (Last 24 out of 32 go into this)
            Accel.X = OldAccel >>> 23;
            if ( Accel.X > 127 )
                Accel.X = -1 * (Accel.X - 128);
            Accel.Y = (OldAccel >>> 15) & 255;
            if ( Accel.Y > 127 )
                Accel.Y = -1 * (Accel.Y - 128);
            Accel.Z = (OldAccel >>> 7) & 255;
            if ( Accel.Z > 127 )
                Accel.Z = -1 * (Accel.Z - 128);
            Accel *= 20;

            OldbRun = ( (OldAccel & 64) != 0 );
			OldbSprint = ( (OldAccel & 32) != 0 );
            NewbPressedJump = ( (OldAccel & 16) != 0 );
            if ( NewbPressedJump )
                bJumpStatus = NewbJumpStatus;
            switch (OldAccel & 7)	// First 3 bits define double click move
            {
                case 0:
                    OldDoubleClickMove = DCLICK_None;
                    break;
                case 1:
                    OldDoubleClickMove = DCLICK_Left;
                    break;
                case 2:
                    OldDoubleClickMove = DCLICK_Right;
                    break;
                case 3:
                    OldDoubleClickMove = DCLICK_Forward;
                    break;
                case 4:
                    OldDoubleClickMove = DCLICK_Back;
                    break;
            }
            //log("Recovered move from "$OldTimeStamp$" acceleration "$Accel$" from "$OldAccel);
            OldTimeStamp = FMin(OldTimeStamp, CurrentTimeStamp + MaxResponseTime);
            MoveAutonomous(OldTimeStamp - CurrentTimeStamp, OldbRun, (bDuck == 1), NewbPressedJump, OldbSprint, (bCrawl == 1), OldDoubleClickMove, Accel, rot(0,0,0));
			CurrentTimeStamp = OldTimeStamp;
        }
    }

    // View components
    ViewPitch = View/32768;
    ViewYaw = 2 * (View - 32768 * ViewPitch);
    ViewPitch *= 2;
// If _RO_
	if( Pawn != none && Pawn.Weapon != none )
	{
	    // Free-aim vars
	    FreeAimPitch = FreeAimRot/32768;
		FreeAimYaw = 2 * (FreeAimRot - 32768 * FreeAimPitch);
		FreeAimPitch *= 2;

		NewFreeAimRot.Pitch  = FreeAimPitch;
		NewFreeAimRot.Yaw  = FreeAimYaw;

		Pawn.Weapon.SetServerOrientation(NewFreeAimRot);
	}
// end _RO_

    // Make acceleration.
    Accel = InAccel * 0.1;

    NewbPressedJump = (bJumpStatus != NewbJumpStatus);
    bJumpStatus = NewbJumpStatus;

    // Save move parameters.
    DeltaTime = FMin(MaxResponseTime,TimeStamp - CurrentTimeStamp);

	if ( Pawn == None )
	{
		ResetTimeMargin();
	}
	else if ( !CheckSpeedHack(DeltaTime) )
	{
		bWasSpeedHack = true;
		DeltaTime = 0;
		Pawn.Velocity = vect(0,0,0);
	}
	else if ( bWasSpeedHack )
	{
		// if have had a speedhack detection, then modify deltatime if getting too far ahead again
		if ( (TimeMargin > 0.5 * Level.MaxTimeMargin) && (Level.MaxTimeMargin > 0) )
			DeltaTime *= 0.8;
	}

    CurrentTimeStamp = TimeStamp;
    ServerTimeStamp = Level.TimeSeconds;
    ViewRot.Pitch = ViewPitch;
    ViewRot.Yaw = ViewYaw;
    ViewRot.Roll = 0;

    if ( NewbPressedJump || (InAccel != vect(0,0,0)) )
		LastActiveTime = Level.TimeSeconds;

	if ( Pawn == None || Pawn.bServerMoveSetPawnRot )
		SetRotation(ViewRot);

	if ( AcknowledgedPawn != Pawn )
		return;

    if ( (Pawn != None) && Pawn.bServerMoveSetPawnRot )
    {
        Rot.Roll = 256 * ClientRoll;
        Rot.Yaw = ViewYaw;
        if ( (Pawn.Physics == PHYS_Swimming) || (Pawn.Physics == PHYS_Flying) )
            maxPitch = 2;
        else
            maxPitch = 0;
        if ( (ViewPitch > maxPitch * RotationRate.Pitch) && (ViewPitch < 65536 - maxPitch * RotationRate.Pitch) )
        {
            If (ViewPitch < 32768)
                Rot.Pitch = maxPitch * RotationRate.Pitch;
            else
                Rot.Pitch = 65536 - maxPitch * RotationRate.Pitch;
        }
        else
            Rot.Pitch = ViewPitch;
        DeltaRot = (Rotation - Rot);
        Pawn.SetRotation(Rot);
    }

    // Perform actual movement
    if ( (Level.Pauser == None) && (DeltaTime > 0) )
        MoveAutonomous(DeltaTime, NewbRun, NewbDuck, NewbPressedJump, NewbSprint, NewbCrawl, DoubleClickMove, Accel, DeltaRot);

    // Accumulate movement error.
    if ( ClientLoc == vect(0,0,0) )
		return;		// first part of double servermove
    else if ( Level.TimeSeconds - LastUpdateTime > 0.3 )
        ClientErr = 10000;
    else if ( Level.TimeSeconds - LastUpdateTime > 180.0/Player.CurrentNetSpeed )
    {
        if ( Pawn == None )
            LocDiff = Location - ClientLoc;
        else
            LocDiff = Pawn.Location - ClientLoc;
        ClientErr = LocDiff Dot LocDiff;
    }

    // If client has accumulated a noticeable positional error, correct him.
    if ( ClientErr > 3 )
    {
        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 ( (Mover(Pawn.Base) != None) || (Vehicle(Pawn.Base) != None) )
                PendingAdjustment.NewLoc = Pawn.Location - Pawn.Base.Location;
            else
                PendingAdjustment.NewLoc = Pawn.Location;
            PendingAdjustment.NewFloor = Pawn.Floor;
        }
    //if ( (ClientErr != 10000) && (Pawn != None) )
//		log(" Client Error at "$TimeStamp$" is "$ClientErr$" with acceleration "$Accel$" LocDiff "$LocDiff$" Physics "$Pawn.Physics);
        LastUpdateTime = Level.TimeSeconds;

		PendingAdjustment.TimeStamp = TimeStamp;
		PendingAdjustment.newState = GetStateName();
    }
	//log("Server moved stamp "$TimeStamp$" location "$Pawn.Location$" Acceleration "$Pawn.Acceleration$" Velocity "$Pawn.Velocity);
}

/* Called on server at end of tick when PendingAdjustment has been set.
Done this way to avoid ever sending more than one ClientAdjustment per server tick.
*/
event SendClientAdjustment()
{
	if ( AcknowledgedPawn != Pawn )
	{
		PendingAdjustment.TimeStamp = 0;
		return;
	}

    if ( (Pawn == None) || (Pawn.Physics != PHYS_Spider) )
    {
        if ( PendingAdjustment.NewVel == vect(0,0,0) )
        {
            ShortClientAdjustPosition
            (
                PendingAdjustment.TimeStamp,
                PendingAdjustment.newState,
                PendingAdjustment.newPhysics,
                PendingAdjustment.NewLoc.X,
                PendingAdjustment.NewLoc.Y,
                PendingAdjustment.NewLoc.Z,
                PendingAdjustment.NewBase
            );
        }
        else
            ClientAdjustPosition
            (
                PendingAdjustment.TimeStamp,
                PendingAdjustment.newState,
                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,
            PendingAdjustment.newState,
            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;
}

// Only executed on server
function ServerDrive(float InForward, float InStrafe, float aUp, bool InJump, int View)
{
    local rotator ViewRotation;

    // Added to handle setting of the correct ViewRotation on the server in network games --Dave@Psyonix
    ViewRotation.Pitch = View/32768;
    ViewRotation.Yaw = 2 * (View - 32768 * ViewRotation.Pitch);
    ViewRotation.Pitch *= 2;
    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;
}

// Modifed to support Red Orchestra movement
final function MoveAutonomous
(
    float DeltaTime,
    bool NewbRun,
    bool NewbDuck,
    bool NewbPressedJump,
	bool NewbSprint,
	bool NewbCrawl,
    eDoubleClickDir DoubleClickMove,
    vector newAccel,
    rotator DeltaRot
)
{
	if ( (Pawn != None) && Pawn.bHardAttach )
		return;

    if ( NewbRun )
        bRun = 1;
    else
        bRun = 0;

    if ( NewbDuck )
        bDuck = 1;
    else
        bDuck = 0;
    bPressedJump = NewbPressedJump;

	if (NewbSprint)
		bSprint = 1;
	else
		bSprint = 0;

	if (NewbCrawl)
		bCrawl = 1;
	else
		bCrawl = 0;

    HandleWalking();
    ProcessMove(DeltaTime, newAccel, DoubleClickMove, DeltaRot);
    if ( Pawn != None )
        Pawn.AutonomousPhysics(DeltaTime);
    else
        AutonomousPhysics(DeltaTime);
    //log("Role "$Role$" moveauto time "$100 * DeltaTime$" ("$Level.TimeDilation$")");
}

/* VeryShortClientAdjustPosition
bandwidth saving version, when velocity is zeroed, and pawn is walking
*/
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);
}

/* ShortClientAdjustPosition
bandwidth saving version, when velocity is zeroed
*/
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);
}

/* ClientAdjustPosition
- pass newloc and newvel in components so they don't get rounded
*/
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);
}

/* LongClientAdjustPosition
long version, when care about pawn's floor normal
*/
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 float NewPing;

	// update ping
	if ( (PlayerReplicationInfo != None) && !bDemoOwner )
	{
		NewPing = FMin(1.5, Level.TimeSeconds - TimeStamp);

		if ( ExactPing < 0.004 )
			ExactPing = FMin(0.3,NewPing);
		else
		{
			if ( NewPing > 2 * ExactPing )
				NewPing = FMin(NewPing, 3*ExactPing);
			ExactPing = FMin(0.99, 0.99 * ExactPing + 0.008 * NewPing); // placebo effect
		}
		PlayerReplicationInfo.Ping = Min(250.0 * ExactPing, 255);
		PlayerReplicationInfo.bReceivedPing = true;
		if ( Level.TimeSeconds - LastPingUpdate > 4 )
		{
			if ( bDynamicNetSpeed && (OldPing > DynamicPingThreshold * 0.001) && (ExactPing > DynamicPingThreshold * 0.001) )
			{
				if ( Level.MoveRepSize < 64 )
					Level.MoveRepSize += 8;
				else if ( Player.CurrentNetSpeed >= 6000 )
					SetNetSpeed(Player.CurrentNetSpeed - 1000);
				OldPing = 0;
			}
			else
				OldPing = ExactPing;
			LastPingUpdate = Level.TimeSeconds;
			ServerUpdatePing(1000 * ExactPing);
		}
	}
    if ( Pawn != None )
    {
        if ( Pawn.bTearOff )
        {
            Pawn = None;
			if ( !IsInState('GameEnded') && !IsInState('RoundEnded') && !IsInState('Dead') )
			{
            	GotoState('Dead');
            }
            return;
        }
        MoveActor = Pawn;
        if ( (ViewTarget != Pawn)
			&& ((ViewTarget == self) || ((Pawn(ViewTarget) != None) && (Pawn(ViewTarget).Health <= 0))) )
		{
			bBehindView = false;
			SetViewTarget(Pawn);
		}
    }
    else
    {
        MoveActor = self;
 	   	if( GetStateName() != newstate )
		{
		    if ( NewState == 'GameEnded' || 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;
    while ( CurrentMove != None )
    {
        if ( CurrentMove.TimeStamp <= CurrentTimeStamp )
        {
            SavedMoves = CurrentMove.NextMove;
            CurrentMove.NextMove = FreeMoves;
            FreeMoves = CurrentMove;
			if ( CurrentMove.TimeStamp == CurrentTimeStamp )
			{
				FreeMoves.Clear();
				if ( ((Mover(NewBase) != None) || (Vehicle(NewBase) != None))
					&& (NewBase == CurrentMove.EndBase) )
				{
					if ( (GetStateName() == NewState)
						&& IsInState('PlayerWalking')
						&& ((MoveActor.Physics == PHYS_Walking) || (MoveActor.Physics == PHYS_Falling)) )
					{
						if ( VSize(CurrentMove.SavedRelativeLocation - NewLocation) < 3 )
						{
							CurrentMove = None;
							return;
						}
						else if ( (Vehicle(NewBase) != None)
								&& (VSize(Velocity) < 3) && (VSize(NewVelocity) < 3)
								&& (VSize(CurrentMove.SavedRelativeLocation - NewLocation) < 30) )
						{
							CurrentMove = None;
							return;
						}
					}
				}
				else if ( (VSize(CurrentMove.SavedLocation - NewLocation) < 3)
					&& (VSize(CurrentMove.SavedVelocity - NewVelocity) < 3)
					&& (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 )
		{
			if ( NewBase != None )
				MoveActor.SetBase(NewBase);
			if ( MoveActor.Base == None )
				MoveActor.bHardAttach = false;
			else
				return;
		}
		else
			return;
	}

	NewFloor.X = NewFloorX;
	NewFloor.Y = NewFloorY;
	NewFloor.Z = NewFloorZ;
	if ( (Mover(NewBase) != None) || (Vehicle(NewBase) != None) )
		NewLocation += NewBase.Location;

	if ( !bDemoOwner )
	{
		//if ( Pawn != None )
		//	log("Client "$Role$" adjust "$self$" stamp "$TimeStamp$" time "$Level.TimeSeconds$" location "$MoveActor.Location);
		MoveActor.bCanTeleport = false;
		if ( !MoveActor.SetLocation(NewLocation) && (Pawn(MoveActor) != None)
			&& (MoveActor.CollisionHeight > Pawn(MoveActor).CrouchHeight)
			&& !Pawn(MoveActor).bIsCrouched
			&& (newPhysics == PHYS_Walking)
			&& (MoveActor.Physics != PHYS_Karma) && (MoveActor.Physics != PHYS_KarmaRagDoll) )
		{
			MoveActor.SetPhysics(newPhysics);
			Pawn(MoveActor).ForceCrouch();
			MoveActor.SetLocation(NewLocation);
		}
		MoveActor.bCanTeleport = true;
	}
	// Hack. Don't let network change physics mode of karma stuff on the client.
	if( (MoveActor.Physics != newPhysics) && (MoveActor.Physics != PHYS_Karma) && (MoveActor.Physics != PHYS_KarmaRagDoll)
		&& (newPhysics != PHYS_Karma) && (newPhysics != PHYS_KarmaRagDoll) )
	{
	    MoveActor.SetPhysics(newPhysics);
	}
	if ( MoveActor != self )
		MoveActor.SetBase(NewBase, NewFloor);

    MoveActor.Velocity = NewVelocity;

    if( GetStateName() != newstate )
        GotoState(newstate);

	bUpdatePosition = true;
}

function ServerUpdatePing(int NewPing)
{
	PlayerReplicationInfo.Ping = Min(0.25 * NewPing, 250);
	PlayerReplicationInfo.bReceivedPing = true;
}

// Modified to support Red Orchestra movement
function ClientUpdatePosition()
{
    local SavedMove CurrentMove;
    local byte realbRun, realbDuck, realbSprint, realbCrawl;
    local bool bRealJump;

	// Dont do any network position updates on things running PHYS_Karma
	if( Pawn != None && (Pawn.Physics == PHYS_Karma || Pawn.Physics == PHYS_KarmaRagDoll) )
		return;

    bUpdatePosition = false;
    realbRun= bRun;
    realbDuck = bDuck;
	realbSprint = bSprint;
	realbCrawl = bCrawl;
    bRealJump = bPressedJump;
    CurrentMove = SavedMoves;
    bUpdating = true;

    while ( CurrentMove != None )
    {
		if ( (PendingMove == CurrentMove) && (Pawn != None) )
			PendingMove.SetInitialPosition(Pawn);
        if ( CurrentMove.TimeStamp <= CurrentTimeStamp )
        {
            SavedMoves = CurrentMove.NextMove;
            CurrentMove.NextMove = FreeMoves;
            FreeMoves = CurrentMove;
            FreeMoves.Clear();
            CurrentMove = SavedMoves;
        }
        else
        {
            MoveAutonomous(CurrentMove.Delta, CurrentMove.bRun, CurrentMove.bDuck, CurrentMove.bPressedJump, CurrentMove.bSprint, CurrentMove.bCrawl, CurrentMove.DoubleClickMove, CurrentMove.Acceleration, rot(0,0,0));
			if ( Pawn != None )
			{
				CurrentMove.SavedLocation = Pawn.Location;
				CurrentMove.SavedVelocity = Pawn.Velocity;
				CurrentMove.EndBase = Pawn.Base;
				if ( (CurrentMove.EndBase != None) && !CurrentMove.EndBase.bWorldGeometry )
					CurrentMove.SavedRelativeLocation = Pawn.Location - CurrentMove.EndBase.Location;
			}
            CurrentMove = CurrentMove.NextMove;
        }
    }

    bUpdating = false;
    bDuck = realbDuck;
    bRun = realbRun;
	bSprint = realbSprint;
	bCrawl = realbCrawl;
    bPressedJump = bRealJump;
}

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;
                }
                return first;
            }
        }
        return Spawn(class'SavedMove');
    }
    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.
//
// Modified to support Red Orchestra movement
function ReplicateMove
(
    float DeltaTime,
    vector NewAccel,
    eDoubleClickDir DoubleClickMove,
    rotator DeltaRot
)
{
    local SavedMove NewMove, OldMove, AlmostLastMove, LastMove;
    local byte ClientRoll;
    local float OldTimeDelta, NetMoveDelta;
    local int OldAccel;
    local vector BuildAccel, AccelNorm, MoveLoc, CompareAccel;
	local bool bPendingJumpStatus;

	MaxResponseTime = Default.MaxResponseTime * Level.TimeDilation;
	DeltaTime = FMin(DeltaTime, MaxResponseTime);

	// find the most recent move, and the most recent interesting move
    if ( SavedMoves != None )
    {
        LastMove = SavedMoves;
        AlmostLastMove = LastMove;
        AccelNorm = Normal(NewAccel);
        while ( LastMove.NextMove != none )
        {
            // find most recent interesting move to send redundantly
            if ( LastMove.IsJumpMove() )
			{
                OldMove = LastMove;
            }
            else if ( (Pawn != None) && ((OldMove == None) || !OldMove.IsJumpMove()) )
            {
				// see if acceleration direction changed
				if ( OldMove != None )
					CompareAccel = Normal(OldMove.Acceleration);
				else
					CompareAccel = AccelNorm;

				if ( (LastMove.Acceleration != CompareAccel) && ((normal(LastMove.Acceleration) Dot CompareAccel) < 0.95) )
					OldMove = LastMove;
			}

            AlmostLastMove = LastMove;
            LastMove = LastMove.NextMove;
        }
        if ( LastMove.IsJumpMove() )
		{
            OldMove = LastMove;
        }
        else if ( (Pawn != None) && ((OldMove == None) || !OldMove.IsJumpMove()) )
        {
			// see if acceleration direction changed
			if ( OldMove != None )
				CompareAccel = Normal(OldMove.Acceleration);
			else
				CompareAccel = AccelNorm;

			if ( (LastMove.Acceleration != CompareAccel) && ((normal(LastMove.Acceleration) Dot CompareAccel) < 0.95) )
				OldMove = LastMove;
		}
    }

    // Get a SavedMove actor to store the movement in.
	NewMove = GetFreeMove();
	if ( NewMove == None )
		return;
	NewMove.SetMoveFor(self, DeltaTime, NewAccel, DoubleClickMove);

    // Simulate the movement locally.
    ProcessMove(NewMove.Delta, NewMove.Acceleration, NewMove.DoubleClickMove, DeltaRot);

	// see if the two moves could be combined
	if ( (PendingMove != None) && (Pawn != None) && (Pawn.Physics == PHYS_Walking)
		&& (NewMove.Delta + PendingMove.Delta < MaxResponseTime)
		&& (NewAccel != vect(0,0,0))
		&& (PendingMove.SavedPhysics == PHYS_Walking)
		&& !PendingMove.bPressedJump && !NewMove.bPressedJump
		&& (PendingMove.bRun == NewMove.bRun)
		&& (PendingMove.bDuck == NewMove.bDuck)
		&& (PendingMove.bSprint == NewMove.bSprint)
		&& (PendingMove.bCrawl == NewMove.bCrawl)
		&& (PendingMove.DoubleClickMove == DCLICK_None)
		&& (NewMove.DoubleClickMove == DCLICK_None)
		&& ((Normal(PendingMove.Acceleration) Dot Normal(NewAccel)) > 0.99)
		&& (Level.TimeDilation >= 0.9))
	{
		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
		if ( (Player.CurrentNetSpeed > 10000) && (GameReplicationInfo != None) && (GameReplicationInfo.PRIArray.Length <= 10) )
			NetMoveDelta = 0.011;
		else
			NetMoveDelta = FMax(0.0222,2 * Level.MoveRepSize/Player.CurrentNetSpeed);

		if ( (Level.TimeSeconds - ClientUpdateTime) * Level.TimeDilation * 0.91 < NetMoveDelta )
		{
			PendingMove = NewMove;
			return;
		}
	}

    ClientUpdateTime = Level.TimeSeconds;

    // check if need to redundantly send previous move
    if ( OldMove != None )
    {
        // old move important to replicate redundantly
        OldTimeDelta = FMin(255, (Level.TimeSeconds - OldMove.TimeStamp) * 500);
        BuildAccel = 0.05 * OldMove.Acceleration + vect(0.5, 0.5, 0.5);
        OldAccel = (CompressAccel(BuildAccel.X) << 23)
                    + (CompressAccel(BuildAccel.Y) << 15)
                    + (CompressAccel(BuildAccel.Z) << 7);
        if ( OldMove.bRun )
            OldAccel += 64;
		if ( OldMove.bSprint )
            OldAccel += 32;
        if ( OldMove.bPressedJump )
            OldAccel += 16;
        OldAccel += OldMove.DoubleClickMove;
    }

    // Send to the server
	ClientRoll = (Rotation.Roll >> 8) & 255;
    if ( PendingMove != None )
    {
		if ( PendingMove.bPressedJump )
			bJumpStatus = !bJumpStatus;
		bPendingJumpStatus = bJumpStatus;
	}
    if ( NewMove.bPressedJump )
         bJumpStatus = !bJumpStatus;

    if ( Pawn == None )
        MoveLoc = Location;
    else
        MoveLoc = Pawn.Location;

    CallServerMove
    (
        NewMove.TimeStamp,
        NewMove.Acceleration * 10,
        MoveLoc,
        NewMove.bRun,
        NewMove.bDuck,
        bPendingJumpStatus,
        bJumpStatus,
		NewMove.bSprint,
		NewMove.bCrawl,
        NewMove.DoubleClickMove,
        ClientRoll,
        (32767 & (Rotation.Pitch/2)) * 32768 + (32767 & (Rotation.Yaw/2)),
        (32767 & (WeaponBufferRotation.Pitch/2)) * 32768 + (32767 & (WeaponBufferRotation.Yaw/2)),
        OldTimeDelta,
        OldAccel
    );
	PendingMove = None;
}
// Converted to handle Red Orchestra movement
function CallServerMove
(
    float TimeStamp,
    vector InAccel,
    vector ClientLoc,
    bool NewbRun,
    bool NewbDuck,
    bool NewbPendingJumpStatus,
    bool NewbJumpStatus,
    bool NewbSprint,
	bool NewbCrawl,
    eDoubleClickDir DoubleClickMove,
    byte ClientRoll,
    int View,
    optional int FreeAimRot,
    optional byte OldTimeDelta,
    optional int OldAccel
)
{
	local byte NewActions0;
	local byte NewActions;
	local bool bCombine;

	// Put together new actions to send (compressed into one byte)
	if (NewbRun)
		NewActions = 1;
	if (NewbDuck)
		NewActions += 2;
	if (NewbJumpStatus)
		NewActions += 4;
	if (NewbSprint)
		NewActions += 8;
	if (NewbCrawl)
		NewActions += 16;

	if ( PendingMove != None )
	{
		if ( PendingMove.bRun )
			NewActions0 = 1;
		if ( PendingMove.bDuck )
			NewActions0 += 2;
		if ( NewbPendingJumpStatus )
			NewActions0 += 4;
		if ( PendingMove.bSprint )
			NewActions0 += 8;
		if ( PendingMove.bCrawl )
			NewActions0 += 16;

		// send two moves simultaneously
		if ( (InAccel == vect(0,0,0))
			&& (PendingMove.StartVelocity == vect(0,0,0))
			&& (DoubleClickMove == DCLICK_None)
			&& (PendingMove.Acceleration == vect(0,0,0)) && (PendingMove.DoubleClickMove == DCLICK_None))
		{
			if ( Pawn == None )
				bCombine = (Velocity == vect(0,0,0));
			else
				bCombine = (Pawn.Velocity == vect(0,0,0));

			if ( bCombine )
			{
				if ( OldTimeDelta == 0 )
				{
					ShortServerMove
					(
						TimeStamp,
						ClientLoc,
						NewActions,
						ClientRoll,
						View,
						FreeAimRot
					);
				}
				else
				{
					ServerMove
					(
						TimeStamp,
						InAccel,
						ClientLoc,
						NewActions,
						DoubleClickMove,
						ClientRoll,
						View,
						FreeAimRot,
						OldTimeDelta,
						OldAccel
					);
				}
				return;
			}
		}

		if ( OldTimeDelta == 0 )
			DualServerMove
			(
				PendingMove.TimeStamp,
				PendingMove.Acceleration * 10,
				NewActions0,
				PendingMove.DoubleClickMove,
				(32767 & (PendingMove.Rotation.Pitch/2)) * 32768 + (32767 & (PendingMove.Rotation.Yaw/2)),
				TimeStamp,
				InAccel,
				ClientLoc,
				NewActions,
				DoubleClickMove,
				ClientRoll,
				View,
				FreeAimRot
			);
		else
			DualServerMove
			(
				PendingMove.TimeStamp,
				PendingMove.Acceleration * 10,
				NewActions0,
				PendingMove.DoubleClickMove,
				(32767 & (PendingMove.Rotation.Pitch/2)) * 32768 + (32767 & (PendingMove.Rotation.Yaw/2)),
				TimeStamp,
				InAccel,
				ClientLoc,
				NewActions,
				DoubleClickMove,
				ClientRoll,
				View,
				FreeAimRot,
				OldTimeDelta,
				OldAccel
			);
	}
	else if ( OldTimeDelta != 0 )
	{
        ServerMove
        (
            TimeStamp,
            InAccel,
            ClientLoc,
            NewActions,
            DoubleClickMove,
            ClientRoll,
            View,
            FreeAimRot,
            OldTimeDelta,
            OldAccel
        );
	}
    else if ( (InAccel == vect(0,0,0)) && (DoubleClickMove == DCLICK_None))
    {
		ShortServerMove
		(
			TimeStamp,
			ClientLoc,
			NewActions,
			ClientRoll,
			View,
			FreeAimRot
		);
    }
    else
		ServerMove
        (
            TimeStamp,
            InAccel,
            ClientLoc,
            NewActions,
            DoubleClickMove,
            ClientRoll,
            View,
            FreeAimRot
        );
}

function HandleWalking()
{
    if ( Pawn != None )
        Pawn.SetWalking( (bRun != 0) && !Region.Zone.IsA('WarpZoneInfo') );
}

function ServerRestartGame()
{
}

function SetFOVAngle(float newFOV)
{
    FOVAngle = newFOV;
}

exec function SetFlashScaling(float F)
{
	ScreenFlashScaling = FClamp(F,0,1);
}

function ClientFlash( float scale, vector fog )
{
    FlashScale = (scale + (1 - ScreenFlashScaling) * (1 - Scale)) * vect(1,1,1);
    flashfog = ScreenFlashScaling * 0.001 * fog;
}

function ClientAdjustGlow( float scale, vector fog )
{
    ConstantGlowScale += scale;
    ConstantGlowFog += 0.001 * fog;
}

function DamageShake(int damage) //send type of damage too!
{
    ClientDamageShake(damage);
}

// function ShakeView( float shaketime, float RollMag, vector OffsetMag, float RollRate, vector OffsetRate, float OffsetTime)

private function ClientDamageShake(int damage)
{
    // todo: add properties!
    ShakeView( Damage * vect(30,0,0),
               120000 * vect(1,0,0),
               0.15 + 0.005 * damage,
               damage * vect(0,0,0.03),
               vect(1,1,1),
               0.2);
}

function WeaponShakeView(vector shRotMag,    vector shRotRate,    float shRotTime,
                   vector shOffsetMag, vector shOffsetRate, float shOffsetTime)
{
	if ( bWeaponViewShake )
		ShakeView(shRotMag * (1.0 - ZoomLevel), shRotRate, shRotTime, shOffsetMag * (1.0 - ZoomLevel), shOffsetRate, shOffsetTime);
}

/* ShakeView()
Call this function to shake the player's view
shaketime = how long to roll view
RollMag = how far to roll view as it shakes
OffsetMag = max view offset
RollRate = how fast to roll view
OffsetRate = how fast to offset view
OffsetTime = how long to offset view (number of shakes)
*/
function ShakeView(vector shRotMag,    vector shRotRate,    float shRotTime,
                   vector shOffsetMag, vector shOffsetRate, float shOffsetTime)
{
    if ( VSize(shRotMag) > VSize(ShakeRotMax) )
    {
        ShakeRotMax  = shRotMag;
        ShakeRotRate = shRotRate;
        ShakeRotTime = shRotTime * vect(1,1,1);
    }

    if ( VSize(shOffsetMag) > VSize(ShakeOffsetMax) )
    {
        ShakeOffsetMax  = shOffsetMag;
        ShakeOffsetRate = shOffsetRate;
        ShakeOffsetTime = shOffsetTime * vect(1,1,1);
    }
}

function StopViewShaking()
{
        ShakeRotMax  = vect(0,0,0);
        ShakeRotRate = vect(0,0,0);
        ShakeRotTime = vect(0,0,0);
		ShakeOffsetMax  = vect(0,0,0);
        ShakeOffsetRate = vect(0,0,0);
        ShakeOffsetTime = vect(0,0,0);
}

//pass in FalloffStartTime = 0 for constant shaking
event SetAmbientShake(float FalloffStartTime, float	FalloffTime, vector	OffsetMag, float OffsetFreq, rotator RotMag, float RotFreq)
{
	bEnableAmbientShake = true;
	AmbientShakeFalloffStartTime = FalloffStartTime;
	AmbientShakeFalloffTime = FalloffTime;
	AmbientShakeOffsetMag = OffsetMag;
	AmbientShakeOffsetFreq = OffsetFreq;
	AmbientShakeRotMag = RotMag;
	AmbientShakeRotFreq = RotFreq;
}

event ShakeViewEvent(vector shRotMag,    vector shRotRate,    float shRotTime, vector shOffsetMag, vector shOffsetRate, float shOffsetTime)
{
	ShakeView(shRotMag, shRotRate, shRotTime, shOffsetMag, shOffsetRate, shOffsetTime);
}

function damageAttitudeTo(pawn Other, float Damage)
{
    if ( (Other != None) && (Other != Pawn) && (Damage > 0) )
        Enemy = Other;
}

function Typing( bool bTyping )
{
    bIsTyping = bTyping;
    if ( (Pawn != None) && !Pawn.bTearOff )
        Pawn.bIsTyping = bIsTyping;

}

//*************************************************************************************
// Normal gameplay execs
// Type the name of the exec function at the console to execute it

exec function Jump( optional float F )
{
    if ( Level.Pauser == PlayerReplicationInfo )
        SetPause(False);
    else
        bPressedJump = true;
}

// 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);
}

// if _RO_
// implemented in ROPlayer
exec function xSpeech(name Type, int Index, PlayerReplicationInfo SquadLeader) {}
// end if _RO_

function ServerSpeech( name Type, int Index, string Callsign )
{
	// log("Type:"$Type@"Index:"$Index@"Callsign:"$Callsign);
	if(PlayerReplicationInfo.VoiceType != None)
		PlayerReplicationInfo.VoiceType.static.PlayerSpeech( Type, Index, Callsign, self );
}

exec function RestartLevel()
{
    if( Level.Netmode==NM_Standalone )
        ClientTravel( "?restart", TRAVEL_Relative, false );
}

exec function LocalTravel( string URL )
{
    if( Level.Netmode==NM_Standalone )
        ClientTravel( URL, TRAVEL_Relative, true );
}

// ------------------------------------------------------------------------
// Loading and saving

/* QuickSave()
Save game to slot 9
*/
exec function QuickSave()
{
    if ( (Pawn != None) && (Pawn.Health > 0)
        && (Level.NetMode == NM_Standalone) )
    {
        ClientMessage(QuickSaveString);
        ConsoleCommand("SaveGame 9");
    }
}

/* QuickLoad()
Load game from slot 9
*/
exec function QuickLoad()
{
    if ( Level.NetMode == NM_Standalone )
        ClientTravel( "?load=9", TRAVEL_Absolute, false);
}

/* SetPause()
 Try to pause game; returns success indicator.
 Replicated to server in network games.
 */
function bool SetPause( BOOL bPause )
{
    bFire = 0;
    bAltFire = 0;
    return Level.Game.SetPause(bPause, self);
}

/* Pause()
Command to try to pause the game.
*/
exec function Pause()
{
	if ( bDemoOwner )
	{
		if ( Level.Pauser == None )
			Level.Pauser = PlayerReplicationInfo;
		else
			Level.Pauser = None;
	}
	else
		ServerPause();
}

function ServerPause()
{
	// Pause if not already
	if(Level.Pauser == None)
		SetPause(true);
	else
		SetPause(false);
}

exec function ShowMenu()
{
	local bool bCloseHUDScreen;

	if ( MyHUD != None )
	{
		bCloseHUDScreen = MyHUD.bShowScoreboard || MyHUD.bShowLocalStats;
		if ( MyHUD.bShowScoreboard )
			MyHUD.bShowScoreboard = false;
		if ( MyHUD.bShowLocalStats )
			MyHUD.bShowLocalStats = false;
		if ( bCloseHUDScreen )
			return;
	}

	ShowMidGameMenu(true);
}

function ShowMidGameMenu(bool bPause)
{
	// Pause if not already
	if(bPause && Level.Pauser == None)
		SetPause(true);

	StopForceFeedback();  // jdf - no way to pause feedback

	// Open menu

	if (bDemoOwner)
		ClientopenMenu(DemoMenuClass);
	else
		ClientOpenMenu(MidGameMenuClass);
}

// Activate specific inventory item
exec function ActivateInventoryItem( class InvItem )
{
    local Powerups Inv;

    Inv = Powerups(Pawn.FindInventoryType(InvItem));
    if ( Inv != None )
        Inv.Activate();
}

// ------------------------------------------------------------------------
// Weapon changing functions

/* ThrowWeapon()
Throw out current weapon, and switch to a new weapon
*/
exec function ThrowWeapon()
{
    if ( (Pawn == None) || (Pawn.Weapon == None) )
        return;

    ServerThrowWeapon();
}

function ServerThrowWeapon()
{
    local Vector TossVel;

    if (Pawn.CanThrowWeapon())
    {
        TossVel = Vector(GetViewRotation());
        TossVel = TossVel * ((Pawn.Velocity dot TossVel) + 150) + Vect(0,0,100);
        Pawn.TossWeapon(TossVel);
        ClientSwitchToBestWeapon();
    }
}

/* PrevWeapon()
- switch to previous inventory group weapon
*/
exec function PrevWeapon()
{
    if ( Level.Pauser != None )
        return;

	if ( Pawn != None )
		Pawn.PrevWeapon();
	else if ( bBehindView )
		CameraDist = FMax(CameraDistRange.Min, CameraDist - 1.0);
}

/* NextWeapon()
- switch to next inventory group weapon
*/
exec function NextWeapon()
{
    if ( Level.Pauser != None )
        return;

	if ( Pawn != None )
		Pawn.NextWeapon();
	else if ( bBehindView )
		CameraDist = FMin(CameraDistRange.Max, CameraDist + 1.0);
}

exec function PipedSwitchWeapon(byte F)
{
	if ( (Pawn == None) || (Pawn.PendingWeapon != None) )
		return;

	SwitchWeapon(F);
}

// The player wants to switch to weapon group number F.
exec function SwitchWeapon(byte F)
{
	if ( Pawn != None )
		Pawn.SwitchWeapon(F);
}

exec function GetWeapon(class<Weapon> NewWeaponClass )
{
    local Inventory Inv;
    local int Count;

    if ( (Pawn == None) || (Pawn.Inventory == None) || (NewWeaponClass == None) )
        return;

    if ( (Pawn.Weapon != None) && (Pawn.Weapon.Class == NewWeaponClass) && (Pawn.PendingWeapon == None) )
    {
        Pawn.Weapon.Reselect();
        return;
    }

    if ( Pawn.PendingWeapon != None && Pawn.PendingWeapon.bForceSwitch )
        return;

    for ( Inv=Pawn.Inventory; Inv!=None; Inv=Inv.Inventory )
    {
        if ( Inv.Class == NewWeaponClass )
        {
            Pawn.PendingWeapon = Weapon(Inv);
            if ( !Pawn.PendingWeapon.HasAmmo() )
            {
                ClientMessage( Pawn.PendingWeapon.ItemName$Pawn.PendingWeapon.MessageNoAmmo );
                Pawn.PendingWeapon = None;
                return;
            }
            Pawn.Weapon.PutDown();
            return;
        }
		Count++;
		if ( Count > 1000 )
			return;
    }
}

// The player wants to select previous item
exec function PrevItem()
{
    local Inventory Inv;
    local Powerups LastItem;

    if ( (Level.Pauser!=None) || (Pawn == None) )
        return;
    if (Pawn.SelectedItem==None)
    {
        Pawn.SelectedItem = Pawn.Inventory.SelectNext();
        Return;
    }
    if (Pawn.SelectedItem.Inventory!=None)
        for( Inv=Pawn.SelectedItem.Inventory; Inv!=None; Inv=Inv.Inventory )
        {
            if (Inv==None) Break;
            if ( Inv.IsA('Powerups') && Powerups(Inv).bActivatable) LastItem=Powerups(Inv);
        }
    for( Inv=Pawn.Inventory; Inv!=Pawn.SelectedItem; Inv=Inv.Inventory )
    {
        if (Inv==None) Break;
        if ( Inv.IsA('Powerups') && Powerups(Inv).bActivatable) LastItem=Powerups(Inv);
    }
    if (LastItem!=None)
        Pawn.SelectedItem = LastItem;
}

// The player wants to active selected item
exec function ActivateItem()
{
    if( Level.Pauser!=None )
        return;
    if ( (Pawn != None) && (Pawn.SelectedItem!=None) )
        Pawn.SelectedItem.Activate();
}

// The player wants to fire.
exec function Fire( optional float F )
{
	if ( Level.NetMode == NM_StandAlone && bViewingMatineeCinematic )
	{
		Level.Game.SceneAbort();
	}

    if ( Level.Pauser == PlayerReplicationInfo )
    {
        SetPause(false);
        return;
    }
	if( bDemoOwner || (Pawn == None) )
		return;

    Pawn.Fire(F);
}

// The player wants to alternate-fire.
exec function AltFire( optional float F )
{
    if ( Level.Pauser == PlayerReplicationInfo )
    {
        SetPause(false);
        return;
    }
	if( bDemoOwner || (Pawn == None) )
		return;
    Pawn.AltFire(F);
}

// The player wants to use something in the level.
exec function Use()
{
    ServerUse();
}

function ServerUse()
{
    local Actor A;
	local Vehicle DrivenVehicle, EntryVehicle, V;

	if ( Role < ROLE_Authority )
		return;

    if ( Level.Pauser == PlayerReplicationInfo )
    {
        SetPause(false);
        return;
    }

    if (Pawn == None || !Pawn.bCanUse)
        return;

	DrivenVehicle = Vehicle(Pawn);
	if( DrivenVehicle != None )
	{
		DrivenVehicle.KDriverLeave(false);
		return;
	}

    // Check for nearby vehicles
    ForEach Pawn.VisibleCollidingActors(class'Vehicle', V, VehicleCheckRadius)
    {
        // Found a vehicle within radius
        EntryVehicle = V.FindEntryVehicle(Pawn);
        if (EntryVehicle != None && EntryVehicle.TryToDrive(Pawn))
            return;
    }

    // Send the 'DoUse' event to each actor player is touching.
    ForEach Pawn.TouchingActors(class'Actor', A)
        A.UsedBy(Pawn);

	if ( Pawn.Base != None )
		Pawn.Base.UsedBy( Pawn );
}

exec function Suicide()
{
	local float MinSuicideInterval;

	if ( Level.NetMode == NM_Standalone )
		MinSuicideInterval = 1;
	else
		MinSuicideInterval = 10;

	if ( (Pawn != None) && (Level.TimeSeconds - Pawn.LastStartTime > MinSuicideInterval) )
		Pawn.Suicide();
}

exec function SetName(coerce string S)
{
//ifdef _KF_
	S = Player.GUIController.SteamGetUserName();
//endif _KF_

	ChangeName(S);
	UpdateURL("Name", S, true);
	SaveConfig();
}

exec function SetVoice( coerce string S )
{
	if ( Level.NetMode == NM_StandAlone )
	{
		if ( PlayerReplicationInfo != None )
			PlayerReplicationInfo.SetCharacterVoice(S);
	}

	else ChangeVoiceType(S);
	UpdateURL("Voice", S, True);
}

function ChangeVoiceType(string NewVoiceType)
{
	if ( VoiceChangeLimit > Level.TimeSeconds )
		return;

	VoiceChangeLimit = Level.TimeSeconds + 10.0;	// TODO - probably better to hook this up to the same limit system that playernames use
	if ( NewVoiceType != "" && PlayerReplicationInfo != None )
		PlayerReplicationInfo.SetCharacterVoice(NewVoiceType);
}

function ChangeName( coerce string S )
{
    if ( Len(S) > 20 )
        S = left(S,20);
    ReplaceText(S, " ", "_");
    ReplaceText(S, "\"", "");
    Level.Game.ChangeName( self, S, true );
}

exec function SwitchTeam()
{
    if ( (PlayerReplicationInfo.Team == None) || (PlayerReplicationInfo.Team.TeamIndex == 1) )
        ServerChangeTeam(0);
    else
        ServerChangeTeam(1);
}

exec function ChangeTeam( int N )
{
	ServerChangeTeam(N);
}

function ServerChangeTeam( int N )
{
    local TeamInfo OldTeam;

    OldTeam = PlayerReplicationInfo.Team;
    Level.Game.ChangeTeam(self, N, true);
    if ( Level.Game.bTeamGame && (PlayerReplicationInfo.Team != OldTeam) )
    {
		if ( (OldTeam != None) && (PlayerReplicationInfo.Team != None)
			&& (PlayerReplicationInfo.Team.Size > OldTeam.Size) )
			Adrenaline = 0;
		if ( Pawn != None )
			Pawn.PlayerChangedTeam();
    }
}


exec function SwitchLevel( string URL )
{
    if( Level.NetMode==NM_Standalone || Level.netMode==NM_ListenServer )
        Level.ServerTravel( URL, false );
}

event ProgressCommand(string Cmd, string Msg1, string Msg2)
{
	local string c,v;

	log(Name$".ProgressCommand Cmd '"$Cmd$"'  Msg1 '"$Msg1$"'   Msg2 '"$Msg2$"'");

	Divide(Cmd, ":", C, V);

	// Checking for bActive prevents multiple status message menus from being opened. However, this also
	// prevents status messages from appearing if another menu is open.
	// Instead, place checks for bActive in places where ProgressCommand is called, but we don't want to override other menus.
	if ( C~="menu" /*&& (!Player.GUIController.bActive)*/ )
		ClientOpenMenu(v, false, Msg1, Msg2);
}


exec function ClearProgressMessages()
{
    local int i;

    for (i=0; i<ArrayCount(ProgressMessage); i++)
    {
        ProgressMessage[i] = "";
        ProgressColor[i] = class'Canvas'.Static.MakeColor(255,255,255);
    }
}

exec event SetProgressMessage( int Index, string S, color C )
{
    if ( Index < ArrayCount(ProgressMessage) )
    {
        ProgressMessage[Index] = S;
        ProgressColor[Index] = C;
    }
}

exec event SetProgressTime( float T )
{
    ProgressTimeOut = T + Level.TimeSeconds;
}

function Restart()
{
    Super.Restart();
    ServerTimeStamp = 0;
	ResetTimeMargin();
    EnterStartState();
    bBehindView = Pawn.PointOfView();
    ClientRestart(Pawn);
    SetViewTarget(Pawn);
}

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 ( IsInState(NewState) )
        BeginState();
    else
        GotoState(NewState);
}

function ClientRestart(Pawn NewPawn)
{
	local bool bNewViewTarget;

	Pawn = NewPawn;
	if ( (Pawn != None) && Pawn.bTearOff )
	{
		Pawn.Controller = None;
		Pawn = None;
	}
	AcknowledgePossession(Pawn);
    if ( Pawn == None )
    {
        GotoState('WaitingForPawn');
        return;
    }
    Pawn.ClientRestart();
    bNewViewTarget = (ViewTarget != Pawn);
    SetViewTarget(Pawn);
    bBehindView = Pawn.PointOfView();
    BehindView(bBehindView);
    if (bNewViewTarget)
	    Pawn.POVChanged(self, false);
    CleanOutSavedMoves();
    EnterStartState();
}

exec function BehindView( Bool B )
{
	if ( Level.NetMode == NM_Standalone || bDemoOwner || Level.Game.bAllowBehindView || Vehicle(Pawn) != None || PlayerReplicationInfo.bOnlySpectator || PlayerReplicationInfo.bAdmin || IsA('Admin') )
	{
		if ( (Vehicle(Pawn)==None) || (Vehicle(Pawn).bAllowViewChange) )	// Allow vehicles to limit view changes
		{
			ClientSetBehindView(B);
			bBehindView = B;
		}
	}
}

exec function ToggleBehindView()
{
	ServerToggleBehindview();
}

function ServerToggleBehindView()
{
	local bool B;

	if ( Level.NetMode == NM_Standalone || Level.Game.bAllowBehindView || Vehicle(Pawn) != None || PlayerReplicationInfo.bOnlySpectator || PlayerReplicationInfo.bAdmin || IsA('Admin') )
	{
		if ( (Vehicle(Pawn)==None) || (Vehicle(Pawn).bAllowViewChange) )	// Allow vehicles to limit view changes
		{
			B = !bBehindView;
			ClientSetBehindView(B);
			bBehindView = B;
		}
	}
}

//=============================================================================
// functions.

// Just changed to pendingWeapon
function ChangedWeapon()
{
    if ( Pawn != None && Pawn.Weapon != None )
    {
        Pawn.Weapon.SetHand(Handedness);
        LastPawnWeapon = Pawn.Weapon.Class;
    }
}

event TravelPostAccept()
{
    if ( Pawn.Health <= 0 )
        Pawn.Health = Pawn.Default.Health;
}

// if _RO_
// Implemented in subclass
simulated function UpdateBlurEffect(float DeltaTime){}
// end _RO_

event PlayerTick( float DeltaTime )
{
	if ( bForcePrecache )
	{
		if ( Level.TimeSeconds > ForcePrecacheTime )
		{
			bForcePrecache = false;
			Level.FillPrecacheMaterialsArray( false );
			Level.FillPrecacheStaticMeshesArray( false );
		}
	}
	else 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();

	if ( !IsSpectating() && Pawn != None )
    	Pawn.RawInput(DeltaTime, aBaseX, aBaseY, aBaseZ, aMouseX, aMouseY, aForward, aTurn, aStrafe, aUp, aLookUp);

    PlayerMove(DeltaTime);

    // if _RO_
    // Update the motion blur effect
    if( Level.NetMode != NM_DedicatedServer )
    {
	if( BlurTime > 0 || ColorFadeTime > 0)
		UpdateBlurEffect(DeltaTime);
    }
    // end _RO_
}

function PlayerMove(float DeltaTime);

//
/* AdjustAim()
Calls this version for player aiming help.
Aimerror not used in this version.
Only adjusts aiming at pawns
*/
function rotator AdjustAim(FireProperties FiredAmmunition, vector projStart, int aimerror)
{
    local vector FireDir, AimSpot, HitNormal, HitLocation, OldAim, AimOffset;
    local actor BestTarget;
    local float bestAim, bestDist, projspeed;
    local actor HitActor;
    local bool bNoZAdjust, bLeading;
    local rotator AimRot;

    FireDir = vector(Rotation);
    if ( FiredAmmunition.bInstantHit )
        HitActor = Trace(HitLocation, HitNormal, projStart + 10000 * FireDir, projStart, true);
    else
        HitActor = Trace(HitLocation, HitNormal, projStart + 4000 * FireDir, projStart, 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 ( (Level.NetMode == NM_Standalone) && bAimingHelp )
        {
            bestAim = 0.93;
            if ( FiredAmmunition.bInstantHit )
                bestAim = 0.97;
            if ( FOVAngle < DefaultFOV - 8 )
                bestAim = 0.99;
        }
        else if ( FiredAmmunition.bInstantHit )
                bestAim = 1.0;
        BestTarget = PickTarget(bestAim, bestDist, FireDir, projStart, FiredAmmunition.MaxRange);
        if ( BestTarget == None )
        {
            if (bBehindView)
                return Pawn.Rotation;
            else
				return Rotation;
        }
        OldAim = projStart + FireDir * bestDist;
    }
	InstantWarnTarget(BestTarget,FiredAmmunition,FireDir);
	ShotTarget = Pawn(BestTarget);
    if ( !bAimingHelp || (Level.NetMode != NM_Standalone) )
    {
        if (bBehindView)
            return Pawn.Rotation;
        else
            return Rotation;
    }

    // aim at target - help with leading also
    if ( !FiredAmmunition.bInstantHit )
    {
        projspeed = FiredAmmunition.ProjectileClass.default.speed;
        BestDist = vsize(BestTarget.Location + BestTarget.Velocity * FMin(1, 0.02 + BestDist/projSpeed) - projStart);
        bLeading = true;
        FireDir = BestTarget.Location + BestTarget.Velocity * FMin(1, 0.02 + BestDist/projSpeed) - projStart;
        AimSpot = projStart + bestDist * Normal(FireDir);
        // if splash damage weapon, try aiming at feet - trace down to find floor
        if ( FiredAmmunition.bTrySplash
            && ((BestTarget.Velocity != vect(0,0,0)) || (BestDist > 1500)) )
        {
            HitActor = Trace(HitLocation, HitNormal, AimSpot - BestTarget.CollisionHeight * vect(0,0,2), AimSpot, false);
            if ( (HitActor != None)
                && FastTrace(HitLocation + vect(0,0,4),projstart) )
                return rotator(HitLocation + vect(0,0,6) - projStart);
        }
    }
    else
    {
        FireDir = BestTarget.Location - projStart;
        AimSpot = projStart + bestDist * Normal(FireDir);
    }
    AimOffset = AimSpot - OldAim;

    // adjust Z of shooter if necessary
    if ( bNoZAdjust || (bLeading && (Abs(AimOffset.Z) < BestTarget.CollisionHeight)) )
        AimSpot.Z = OldAim.Z;
    else if ( AimOffset.Z < 0 )
        AimSpot.Z = BestTarget.Location.Z + 0.4 * BestTarget.CollisionHeight;
    else
        AimSpot.Z = BestTarget.Location.Z - 0.7 * BestTarget.CollisionHeight;

    if ( !bLeading )
    {
        // if not leading, add slight random error ( significant at long distances )
        if ( !bNoZAdjust )
        {
            AimRot = rotator(AimSpot - projStart);
            if ( FOVAngle < DefaultFOV - 8 )
                AimRot.Yaw = AimRot.Yaw + 200 - Rand(400);
            else
                AimRot.Yaw = AimRot.Yaw + 375 - Rand(750);
            return AimRot;
        }
    }
    else if ( !FastTrace(projStart + 0.9 * bestDist * Normal(FireDir), projStart) )
    {
        FireDir = BestTarget.Location - projStart;
        AimSpot = projStart + bestDist * Normal(FireDir);
    }

    return rotator(AimSpot - projStart);
}

function bool NotifyLanded(vector HitNormal)
{
    return bUpdating;
}

//=============================================================================
// Player Control

// Player view.
// Compute the rendering viewpoint for the player.
//

function AdjustView(float DeltaTime )
{
    // teleporters affect your FOV, so adjust it back down
    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;
    }

    // adjust FOV for weapon zooming
    if ( bZooming )
    {
        ZoomLevel = FMin(ZoomLevel + DeltaTime, DesiredZoomLevel);
        DesiredFOV = FClamp(90.0 - (ZoomLevel * 88.0), 1, 170);
    }
}

function CalcBehindView(out vector CameraLocation, out rotator CameraRotation, float Dist)
{
    local vector View,HitLocation,HitNormal;
    local float ViewDist,RealDist;
    local vector globalX,globalY,globalZ;
    local vector localX,localY,localZ;

    CameraRotation = Rotation;
    CameraRotation.Roll = 0;
	CameraLocation.Z += 12;

    // add view rotation offset to cameraview (amb)
    CameraRotation += CameraDeltaRotation;

    View = vect(1,0,0) >> CameraRotation;

    // add view radius offset to camera location and move viewpoint up from origin (amb)
    RealDist = Dist;
    Dist += CameraDeltaRad;

    if( Trace( HitLocation, HitNormal, CameraLocation - Dist * vector(CameraRotation), CameraLocation,false,vect(10,10,10) ) != None )
        ViewDist = FMin( (CameraLocation - HitLocation) Dot View, Dist );
    else
        ViewDist = Dist;

    if ( !bBlockCloseCamera || !bValidBehindCamera || (ViewDist > 10 + FMax(ViewTarget.CollisionRadius, ViewTarget.CollisionHeight)) )
	{
		//Log("Update Cam ");
		bValidBehindCamera = true;
		OldCameraLoc = CameraLocation - ViewDist * View;
		OldCameraRot = CameraRotation;
	}
	else
	{
		//Log("Dont Update Cam "$bBlockCloseCamera@bValidBehindCamera@ViewDist);
		SetRotation(OldCameraRot);
	}

    CameraLocation = OldCameraLoc;
    CameraRotation = OldCameraRot;

    // add view swivel rotation to cameraview (amb)
    GetAxes(CameraSwivel,globalX,globalY,globalZ);
    localX = globalX >> CameraRotation;
    localY = globalY >> CameraRotation;
    localZ = globalZ >> CameraRotation;
    CameraRotation = OrthoRotation(localX,localY,localZ);
}

function CalcFirstPersonView( out vector CameraLocation, out rotator CameraRotation )
{
    local vector x, y, z, AmbShakeOffset;
	local rotator AmbShakeRot;
	local float FalloffScaling;

    GetAxes(Rotation, x, y, z);

	if(bEnableAmbientShake)
	{
		if (AmbientShakeFalloffStartTime > 0 && Level.TimeSeconds - AmbientShakeFalloffStartTime > AmbientShakeFalloffTime)
			bEnableAmbientShake = false;
		else
		{
			if (AmbientShakeFalloffStartTime > 0)
			{
		FalloffScaling = 1.0 - ((Level.TimeSeconds - AmbientShakeFalloffStartTime) / AmbientShakeFalloffTime);
		FalloffScaling = FClamp(FalloffScaling, 0.0, 1.0);
			}
			else
				FalloffScaling = 1.0;

		AmbShakeOffset = AmbientShakeOffsetMag * FalloffScaling *
			sin(Level.TimeSeconds * AmbientShakeOffsetFreq * 2 * Pi);

		AmbShakeRot = AmbientShakeRotMag * FalloffScaling *
			sin(Level.TimeSeconds * AmbientShakeRotFreq * 2 * Pi);
	}
	}

    // First-person view.
    CameraRotation = Normalize(Rotation + ShakeRot + AmbShakeRot); // amb
    CameraLocation = CameraLocation + Pawn.EyePosition() + Pawn.WalkBob +
                     ShakeOffset.X * x +
                     ShakeOffset.Y * y +
                     ShakeOffset.Z * z +
					 AmbShakeOffset;
}

event AddCameraEffect(CameraEffect NewEffect,optional bool RemoveExisting)
{
    if(RemoveExisting)
        RemoveCameraEffect(NewEffect);

    CameraEffects.Length = CameraEffects.Length + 1;
    CameraEffects[CameraEffects.Length - 1] = NewEffect;
}

event RemoveCameraEffect(CameraEffect ExEffect)
{
    local int   EffectIndex;

    for(EffectIndex = 0;EffectIndex < CameraEffects.Length;EffectIndex++)
        if(CameraEffects[EffectIndex] == ExEffect)
        {
            CameraEffects.Remove(EffectIndex,1);
            return;
        }
}

exec function CreateCameraEffect(class<CameraEffect> EffectClass)
{
    AddCameraEffect(new EffectClass);
}

simulated function rotator GetViewRotation()
{
    if ( bBehindView && (Pawn != None) )
        return Pawn.Rotation;
    return Rotation;
}

function CacheCalcView(actor ViewActor, vector CameraLocation, rotator CameraRotation)
{
	CalcViewActor		= ViewActor;
	if (ViewActor != None)
		CalcViewActorLocation = ViewActor.Location;
	CalcViewLocation	= CameraLocation;
	CalcViewRotation	= CameraRotation;
	LastPlayerCalcView	= Level.TimeSeconds;
}

event PlayerCalcView(out actor ViewActor, out vector CameraLocation, out rotator CameraRotation )
{
    local Pawn PTarget;

	if ( LastPlayerCalcView == Level.TimeSeconds && CalcViewActor != None && CalcViewActor.Location == CalcViewActorLocation )
	{
		ViewActor	= CalcViewActor;
		CameraLocation	= CalcViewLocation;
		CameraRotation	= CalcViewRotation;
		return;
	}

	// If desired, call the pawn's own special callview
	if( Pawn != None && Pawn.bSpecialCalcView && (ViewTarget == Pawn) )
	{
		// try the 'special' calcview. This may return false if its not applicable, and we do the usual.
		if ( Pawn.SpecialCalcView(ViewActor, CameraLocation, CameraRotation) )
		{
			CacheCalcView(ViewActor,CameraLocation,CameraRotation);
			return;
		}
	}

    if ( (ViewTarget == None) || ViewTarget.bDeleteMe )
    {
        if ( bViewBot && (CheatManager != None) )
			CheatManager.ViewBot();
        else if ( (Pawn != None) && !Pawn.bDeleteMe )
            SetViewTarget(Pawn);
        else if ( RealViewTarget != None )
            SetViewTarget(RealViewTarget);
        else
            SetViewTarget(self);
    }

    ViewActor = ViewTarget;
    CameraLocation = ViewTarget.Location;

    if ( ViewTarget == Pawn )
    {
        if( bBehindView ) //up and behind
            CalcBehindView(CameraLocation, CameraRotation, CameraDist * Pawn.Default.CollisionRadius);
        else
            CalcFirstPersonView( CameraLocation, CameraRotation );

		CacheCalcView(ViewActor,CameraLocation,CameraRotation);
        return;
    }
    if ( ViewTarget == self )
    {
        if ( bCameraPositionLocked )
            CameraRotation = CheatManager.LockedRotation;
        else
            CameraRotation = Rotation;

		CacheCalcView(ViewActor,CameraLocation,CameraRotation);
        return;
    }

    if ( ViewTarget.IsA('Projectile') )
    {
        if ( Projectile(ViewTarget).bSpecialCalcView && Projectile(ViewTarget).SpecialCalcView(ViewActor, CameraLocation, CameraRotation, bBehindView) )
        {
            CacheCalcView(ViewActor,CameraLocation,CameraRotation);
            return;
        }

        if ( !bBehindView )
        {
            CameraLocation += (ViewTarget.CollisionHeight) * vect(0,0,1);
            CameraRotation = Rotation;

    		CacheCalcView(ViewActor,CameraLocation,CameraRotation);
            return;
        }
    }

    CameraRotation = ViewTarget.Rotation;
    PTarget = Pawn(ViewTarget);
    if ( PTarget != None )
    {
        if ( (Level.NetMode == NM_Client) || (bDemoOwner && (Level.NetMode != NM_Standalone)) )
        {
            PTarget.SetViewRotation(TargetViewRotation);
            CameraRotation = BlendedTargetViewRotation;

            PTarget.EyeHeight = TargetEyeHeight;
        }
        else if ( PTarget.IsPlayerPawn() )
            CameraRotation = PTarget.GetViewRotation();

		if (PTarget.bSpecialCalcView && PTarget.SpectatorSpecialCalcView(self, ViewActor, CameraLocation, CameraRotation))
		{
			CacheCalcView(ViewActor, CameraLocation, CameraRotation);
			return;
		}

        if ( !bBehindView )
            CameraLocation += PTarget.EyePosition();
    }
    if ( bBehindView )
    {
        CameraLocation = CameraLocation + (ViewTarget.Default.CollisionHeight - ViewTarget.CollisionHeight) * vect(0,0,1);
        CalcBehindView(CameraLocation, CameraRotation, CameraDist * ViewTarget.Default.CollisionRadius);
    }

	CacheCalcView(ViewActor,CameraLocation,CameraRotation);
}

function int BlendRot(float DeltaTime, int BlendC, int NewC)
{
	if ( Abs(BlendC - NewC) > 32767 )
	{
		if ( BlendC > NewC )
			NewC += 65536;
		else
			BlendC += 65536;
	}
	if ( Abs(BlendC - NewC) > 4096 )
		BlendC = NewC;
	else
		BlendC = BlendC + (NewC - BlendC) * FMin(1,24 * DeltaTime);

	return (BlendC & 65535);
}

function CheckShake(out float MaxOffset, out float Offset, out float Rate, out float Time, float dt)
{
    if ( abs(Offset) < abs(MaxOffset) )
        return;

    Offset = MaxOffset;
    if ( Time > 1 )
    {
        if ( Time * abs(MaxOffset/Rate) <= 1 )
            MaxOffset = MaxOffset * (1/Time - 1);
        else
            MaxOffset *= -1;
        Time -= dt;
        Rate *= -1;
    }
    else
    {
        MaxOffset = 0;
        Offset = 0;
        Rate = 0;
    }
}

function UpdateShakeRotComponent(out float max, out int current, out float rate, out float time, float dt)
{
    local float fCurrent;

    current = ((current & 65535) + rate * dt) & 65535;
    if ( current > 32768 )
    current -= 65536;

    fCurrent = current;
    CheckShake(max, fCurrent, rate, time, dt);
    current = fCurrent;
}

function ViewShake(float DeltaTime)
{
    if ( ShakeOffsetRate != vect(0,0,0) )
    {
        // modify shake offset
        ShakeOffset.X += DeltaTime * ShakeOffsetRate.X;
        CheckShake(ShakeOffsetMax.X, ShakeOffset.X, ShakeOffsetRate.X, ShakeOffsetTime.X, DeltaTime);

        ShakeOffset.Y += DeltaTime * ShakeOffsetRate.Y;
        CheckShake(ShakeOffsetMax.Y, ShakeOffset.Y, ShakeOffsetRate.Y, ShakeOffsetTime.Y, DeltaTime);

        ShakeOffset.Z += DeltaTime * ShakeOffsetRate.Z;
        CheckShake(ShakeOffsetMax.Z, ShakeOffset.Z, ShakeOffsetRate.Z, ShakeOffsetTime.Z, DeltaTime);
    }

    if ( ShakeRotRate != vect(0,0,0) )
    {
        UpdateShakeRotComponent(ShakeRotMax.X, ShakeRot.Pitch, ShakeRotRate.X, ShakeRotTime.X, DeltaTime);
        UpdateShakeRotComponent(ShakeRotMax.Y, ShakeRot.Yaw,   ShakeRotRate.Y, ShakeRotTime.Y, DeltaTime);
        UpdateShakeRotComponent(ShakeRotMax.Z, ShakeRot.Roll,  ShakeRotRate.Z, ShakeRotTime.Z, DeltaTime);
    }
}

function bool TurnTowardNearestEnemy();

function TurnAround()
{
    if ( !bSetTurnRot )
    {
        TurnRot180 = Rotation;
        TurnRot180.Yaw += 32768;
        bSetTurnRot = true;
    }

    DesiredRotation = TurnRot180;
    bRotateToDesired = ( DesiredRotation.Yaw != Rotation.Yaw );
}

function UpdateRotation(float DeltaTime, float maxPitch)
{
    local rotator newRotation, ViewRotation;

    if ( bInterpolating || ((Pawn != None) && Pawn.bInterpolating) )
    {
        ViewShake(deltaTime);
        return;
    }

    // Added FreeCam control for better view control
    if (bFreeCam == True)
    {
        if (bFreeCamZoom == True)
        {
            CameraDeltaRad += DeltaTime * 0.25 * aLookUp;
        }
        else if (bFreeCamSwivel == True)
        {
            CameraSwivel.Yaw += 16.0 * DeltaTime * aTurn;
            CameraSwivel.Pitch += 16.0 * DeltaTime * aLookUp;
        }
        else
        {
            CameraDeltaRotation.Yaw += 32.0 * DeltaTime * aTurn;
            CameraDeltaRotation.Pitch += 32.0 * DeltaTime * aLookUp;
        }
    }
    else
    {
        ViewRotation = Rotation;

		if(Pawn != None && Pawn.Physics != PHYS_Flying) // mmmmm
		{
			// Ensure we are not setting the pawn to a rotation beyond its desired
			if(	Pawn.DesiredRotation.Roll < 65535 &&
				(ViewRotation.Roll < Pawn.DesiredRotation.Roll || ViewRotation.Roll > 0))
				ViewRotation.Roll = 0;
			else if( Pawn.DesiredRotation.Roll > 0 &&
				(ViewRotation.Roll > Pawn.DesiredRotation.Roll || ViewRotation.Roll < 65535))
				ViewRotation.Roll = 0;
		}

        DesiredRotation = ViewRotation; //save old rotation

        if ( bTurnToNearest != 0 )
            TurnTowardNearestEnemy();
        else if ( bTurn180 != 0 )
            TurnAround();
        else
        {
            TurnTarget = None;
            bRotateToDesired = false;
            bSetTurnRot = false;
            ViewRotation.Yaw += 32.0 * DeltaTime * aTurn;
            ViewRotation.Pitch += 32.0 * DeltaTime * aLookUp;
        }
        if (Pawn != None)
	        ViewRotation.Pitch = Pawn.LimitPitch(ViewRotation.Pitch);

        SetRotation(ViewRotation);

        ViewShake(deltaTime);
        ViewFlash(deltaTime);

        NewRotation = ViewRotation;
        //NewRotation.Roll = Rotation.Roll;

        if ( !bRotateToDesired && (Pawn != None) && (!bFreeCamera || !bBehindView) )
            Pawn.FaceRotation(NewRotation, deltatime);
    }
}

function ClearDoubleClick()
{
    if (PlayerInput != None)
        PlayerInput.DoubleClickTimer = 0.0;
}

simulated function bool DodgingIsEnabled()
{
	if ( PlayerInput != None )
		return PlayerInput.bEnableDodging;
	else if ( InputClass != None )
		return InputClass.default.bEnableDodging;
	else return class'Engine.PlayerInput'.default.bEnableDodging;
}

simulated function SetDodging( bool Enabled )
{
    if( PlayerInput != None)
        PlayerInput.bEnableDodging = Enabled;

    InputClass.default.bEnableDodging = Enabled;
    InputClass.static.StaticSaveConfig();
}

// Player movement.
// Player Standing, walking, running, falling.
state PlayerWalking
{
ignores SeePlayer, HearNoise, Bump;

    function bool NotifyPhysicsVolumeChange( PhysicsVolume NewVolume )
    {
        if ( NewVolume.bWaterVolume )
            GotoState(Pawn.WaterMovementState);
        return false;
    }

    function ProcessMove(float DeltaTime, vector NewAccel, eDoubleClickDir DoubleClickMove, rotator DeltaRot)
    {
        local vector OldAccel;
        local bool OldCrouch;

		if ( Pawn == None )
			return;
        OldAccel = Pawn.Acceleration;
        if ( Pawn.Acceleration != NewAccel )
			Pawn.Acceleration = NewAccel;
		if ( bDoubleJump && (bUpdating || Pawn.CanDoubleJump()) )
			Pawn.DoDoubleJump(bUpdating);
        else if ( bPressedJump )
			Pawn.DoJump(bUpdating);

        Pawn.SetViewPitch(Rotation.Pitch);

        if ( Pawn.Physics != PHYS_Falling )
        {
            OldCrouch = Pawn.bWantsToCrouch;
            if (bDuck == 0)
                Pawn.ShouldCrouch(false);
            else if ( Pawn.bCanCrouch )
                Pawn.ShouldCrouch(true);
        }
    }

    function PlayerMove( float DeltaTime )
    {
        local vector X,Y,Z, NewAccel;
        local eDoubleClickDir DoubleClickMove;
        local rotator OldRotation, ViewRotation;
        local bool  bSaveJump;

        if( Pawn == None )
        {
            GotoState('Dead'); // this was causing instant respawns in mp games
            return;
        }

        GetAxes(Pawn.Rotation,X,Y,Z);

        // Update acceleration.
        NewAccel = aForward*X + aStrafe*Y;
        NewAccel.Z = 0;
        if ( VSize(NewAccel) < 1.0 )
            NewAccel = vect(0,0,0);
        DoubleClickMove = PlayerInput.CheckForDoubleClickMove(1.1*DeltaTime/Level.TimeDilation);

        GroundPitch = 0;
        ViewRotation = Rotation;
        if ( Pawn.Physics == PHYS_Walking )
        {
            // tell pawn about any direction changes to give it a chance to play appropriate animation
            //if walking, look up/down stairs - unless player is rotating view
             if ( (bLook == 0)
                && (((Pawn.Acceleration != Vect(0,0,0)) && bSnapToLevel) || !bKeyboardLook) )
            {
                if ( bLookUpStairs || bSnapToLevel )
                {
                    GroundPitch = FindStairRotation(deltaTime);
                    ViewRotation.Pitch = GroundPitch;
                }
                else if ( bCenterView )
                {
                    ViewRotation.Pitch = ViewRotation.Pitch & 65535;
                    if (ViewRotation.Pitch > 32768)
                        ViewRotation.Pitch -= 65536;
                    ViewRotation.Pitch = ViewRotation.Pitch * (1 - 12 * FMin(0.0833, deltaTime));
                    if ( (Abs(ViewRotation.Pitch) < 250) && (ViewRotation.Pitch < 100) )
                        ViewRotation.Pitch = -249;
                }
            }
        }
        else
        {
            if ( !bKeyboardLook && (bLook == 0) && bCenterView )
            {
                ViewRotation.Pitch = ViewRotation.Pitch & 65535;
                if (ViewRotation.Pitch > 32768)
                    ViewRotation.Pitch -= 65536;
                ViewRotation.Pitch = ViewRotation.Pitch * (1 - 12 * FMin(0.0833, deltaTime));
                if ( (Abs(ViewRotation.Pitch) < 250) && (ViewRotation.Pitch < 100) )
                    ViewRotation.Pitch = -249;
            }
        }
        Pawn.CheckBob(DeltaTime, Y);

        // Update rotation.
        SetRotation(ViewRotation);
        OldRotation = Rotation;
        UpdateRotation(DeltaTime, 1);
		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;
    }

    function BeginState()
    {
       	DoubleClickDir = DCLICK_None;
       	bPressedJump = false;
       	GroundPitch = 0;
		if ( Pawn != None )
		{
            if ( Pawn.Mesh == None )
                Pawn.SetMesh();
            Pawn.ShouldCrouch(false);
            if (Pawn.Physics != PHYS_Falling && Pawn.Physics != PHYS_Karma) // FIXME HACK!!!
                Pawn.SetPhysics(PHYS_Walking);
		}
     }

    function EndState()
    {
        GroundPitch = 0;
        if ( Pawn != None && bDuck==0 )
            Pawn.ShouldCrouch(false);
    }

Begin:
}

// player is climbing ladder
state PlayerClimbing
{
ignores SeePlayer, HearNoise, Bump;

    function bool NotifyPhysicsVolumeChange( PhysicsVolume NewVolume )
    {
        if ( NewVolume.bWaterVolume )
            GotoState(Pawn.WaterMovementState);
        else
            GotoState(Pawn.LandMovementState);
        return false;
    }

    function ProcessMove(float DeltaTime, vector NewAccel, eDoubleClickDir DoubleClickMove, rotator DeltaRot)
    {
        local vector OldAccel;

        OldAccel = Pawn.Acceleration;
        if ( Pawn.Acceleration != NewAccel )
			Pawn.Acceleration = NewAccel;

        if ( bPressedJump )
        {
            Pawn.DoJump(bUpdating);
            if ( Pawn.Physics == PHYS_Falling )
                GotoState('PlayerWalking');
        }
    }

    function PlayerMove( float DeltaTime )
    {
        local vector X,Y,Z, NewAccel;
        local eDoubleClickDir DoubleClickMove;
        local rotator OldRotation, ViewRotation;
        local bool  bSaveJump;

        GetAxes(Rotation,X,Y,Z);

        // Update acceleration.
        if ( Pawn.OnLadder != None )
        {
            NewAccel = aForward*Pawn.OnLadder.ClimbDir;
            if ( Pawn.OnLadder.bAllowLadderStrafing )
				NewAccel += aStrafe*Y;
		}
        else
            NewAccel = aForward*X + aStrafe*Y;
        if ( VSize(NewAccel) < 1.0 )
            NewAccel = vect(0,0,0);

        ViewRotation = Rotation;

        // Update rotation.
        SetRotation(ViewRotation);
        OldRotation = Rotation;
        UpdateRotation(DeltaTime, 1);

        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;
    }

    function BeginState()
    {
        Pawn.ShouldCrouch(false);
        Pawn.ShouldProne(false);
        bPressedJump = false;
    }

    function EndState()
    {
        if ( Pawn != None )
        {
            Pawn.ShouldCrouch(false);
            Pawn.ShouldProne(false);
        }
    }
}

// Player movement.
// Player Driving a Karma 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)
            return;

		//log("Forward:"@InForward@" Strafe:"@InStrafe@" Up:"@InUp);

		CurrentVehicle.Throttle = FClamp( InForward/5000.0, -1.0, 1.0 );
		CurrentVehicle.Steering = FClamp( -InStrafe/5000.0, -1.0, 1.0 );
		CurrentVehicle.Rise = FClamp( InUp/5000.0, -1.0, 1.0 );
	}

    function PlayerMove( float DeltaTime )
    {
		local Vehicle CurrentVehicle;
		local float NewPing;

		CurrentVehicle = Vehicle(Pawn);

		// update 'looking' rotation
        UpdateRotation(DeltaTime, 2);

        // TODO: Don't send things like aForward and aStrafe for gunners who don't need it
		// Only servers can actually do the driving logic.
        if (Role < ROLE_Authority )
        {
			if ( (Level.TimeSeconds - LastPingUpdate > 4) && (PlayerReplicationInfo != None) && !bDemoOwner )
			{
				LastPingUpdate = Level.TimeSeconds;
				NewPing = float(ConsoleCommand("GETPING"));
				if ( ExactPing < 0.006 )
					ExactPing = FMin(0.1,0.001 * NewPing);
				else
					ExactPing = 0.99 * ExactPing + 0.0001 * NewPing;
				PlayerReplicationInfo.Ping = Min(250.0 * ExactPing, 255);
				PlayerReplicationInfo.bReceivedPing = true;
				OldPing = ExactPing;
				ServerUpdatePing(1000 * ExactPing);
			}
            if (!bSkippedLastUpdate &&                              // in order to skip this update we must not have skipped the last one
                (Player.CurrentNetSpeed < 10000) &&                 // and netspeed must be low
                (Level.TimeSeconds - ClientUpdateTime < 0.0222) &&  // and time since last update must be short
                bPressedJump == bLastPressedJump &&                 // and update must not contain major changes
                aUp - aLastUp < 0.01 &&                             // "
                aForward - aLastForward < 0.01 &&                   // "
                aStrafe - aLastStrafe < 0.01                        // "
               )
            {
//                log("!bSkippedLastUpdate: "$!bSkippedLastUpdate);
//                log("(Player.CurrentNetSpeed < 10000): "$(Player.CurrentNetSpeed < 10000));
//                log("(Level.TimeSeconds - ClientUpdateTime < 0.0222): "$(Level.TimeSeconds - ClientUpdateTime < 0.0222)$"  - "$Level.TimeSeconds - ClientUpdateTime);
//                log("bPressedJump == bLastPressedJump: "$bPressedJump == bLastPressedJump);
//                log("aUp - aLastUp < 0.01: "$aUp - aLastUp < 0.01);
//                log("aForward - aLastForward < 0.01: "$aForward - aLastForward < 0.01);
//                log("aStrafe - aLastStrafe < 0.01: "$aStrafe - aLastStrafe < 0.01);

                bSkippedLastUpdate = True;
                return;
            }
            else
            {
                bSkippedLastUpdate = False;
                ClientUpdateTime = Level.TimeSeconds;

                // Save Move
                bLastPressedJump = bPressedJump;
                aLastUp = aUp;
                aLastForward = aForward;
                aLastStrafe = aStrafe;

                if (CurrentVehicle != None)
                {
                    CurrentVehicle.Throttle = FClamp( aForward/5000.0, -1.0, 1.0 );
                    CurrentVehicle.Steering = FClamp( -aStrafe/5000.0, -1.0, 1.0 );
                    CurrentVehicle.Rise = FClamp( aUp/5000.0, -1.0, 1.0 );
                }

                ServerDrive(aForward, aStrafe, aUp, bPressedJump, (32767 & (Rotation.Pitch/2)) * 32768 + (32767 & (Rotation.Yaw/2)));
            }
        }
		else
			ProcessDrive(aForward, aStrafe, aUp, bPressedJump);

		// If the vehicle is being controlled here - set replicated variables.
		if (CurrentVehicle != None)
		{
			if(bFire == 0 && CurrentVehicle.bWeaponIsFiring)
				CurrentVehicle.ClientVehicleCeaseFire(False);

			if(bAltFire == 0 && CurrentVehicle.bWeaponIsAltFiring)
				CurrentVehicle.ClientVehicleCeaseFire(True);
		}
    }

	function BeginState()
	{
		PlayerReplicationInfo.bReceivedPing = false;
		CleanOutSavedMoves();
	}

	function EndState()
	{
		CleanOutSavedMoves();
	}
}

// Player movement.
// Player walking on walls
state PlayerSpidering
{
ignores SeePlayer, HearNoise, Bump;

    event bool NotifyHitWall(vector HitNormal, actor HitActor)
    {
        Pawn.SetPhysics(PHYS_Spider);
        Pawn.SetBase(HitActor, HitNormal);
        return true;
    }

    // if spider mode, update rotation based on floor
    function UpdateRotation(float DeltaTime, float maxPitch)
    {
        local rotator ViewRotation;
        local vector MyFloor, CrossDir, FwdDir, OldFwdDir, OldX, RealFloor;

        if ( bInterpolating || Pawn.bInterpolating )
        {
            ViewShake(deltaTime);
            return;
        }

        TurnTarget = None;
        bRotateToDesired = false;
        bSetTurnRot = false;

        if ( (Pawn.Base == None) || (Pawn.Floor == vect(0,0,0)) )
            MyFloor = vect(0,0,1);
        else
            MyFloor = Pawn.Floor;

        if ( MyFloor != OldFloor )
        {
            // smoothly change floor
            RealFloor = MyFloor;
            MyFloor = Normal(6*DeltaTime * MyFloor + (1 - 6*DeltaTime) * OldFloor);
            if ( (RealFloor Dot MyFloor) > 0.999 )
                MyFloor = RealFloor;
			else
			{
				// translate view direction
				CrossDir = Normal(RealFloor Cross OldFloor);
				FwdDir = CrossDir Cross MyFloor;
				OldFwdDir = CrossDir Cross OldFloor;
				ViewX = MyFloor * (OldFloor Dot ViewX)
							+ CrossDir * (CrossDir Dot ViewX)
							+ FwdDir * (OldFwdDir Dot ViewX);
				ViewX = Normal(ViewX);

				ViewZ = MyFloor * (OldFloor Dot ViewZ)
							+ CrossDir * (CrossDir Dot ViewZ)
							+ FwdDir * (OldFwdDir Dot ViewZ);
				ViewZ = Normal(ViewZ);
				OldFloor = MyFloor;
				ViewY = Normal(MyFloor Cross ViewX);
			}
        }

        if ( (aTurn != 0) || (aLookUp != 0) )
        {
            // adjust Yaw based on aTurn
            if ( aTurn != 0 )
                ViewX = Normal(ViewX + 2 * ViewY * Sin(0.0005*DeltaTime*aTurn));

            // adjust Pitch based on aLookUp
            if ( aLookUp != 0 )
            {
                OldX = ViewX;
                ViewX = Normal(ViewX + 2 * ViewZ * Sin(0.0005*DeltaTime*aLookUp));
                ViewZ = Normal(ViewX Cross ViewY);

                // bound max pitch
                if ( (ViewZ Dot MyFloor) < 0.707   )
                {
                    OldX = Normal(OldX - MyFloor * (MyFloor Dot OldX));
                    if ( (ViewX Dot MyFloor) > 0)
                        ViewX = Normal(OldX + MyFloor);
                    else
                        ViewX = Normal(OldX - MyFloor);

                    ViewZ = Normal(ViewX Cross ViewY);
                }
            }

            // calculate new Y axis
            ViewY = Normal(MyFloor Cross ViewX);
        }
        ViewRotation =  OrthoRotation(ViewX,ViewY,ViewZ);
        SetRotation(ViewRotation);
        ViewShake(deltaTime);
        ViewFlash(deltaTime);
        Pawn.FaceRotation(ViewRotation, deltaTime );
    }

    function bool NotifyLanded(vector HitNormal)
    {
        Pawn.SetPhysics(PHYS_Spider);
        return bUpdating;
    }

    function bool NotifyPhysicsVolumeChange( PhysicsVolume NewVolume )
    {
        if ( NewVolume.bWaterVolume )
            GotoState(Pawn.WaterMovementState);
        return false;
    }

    function ProcessMove(float DeltaTime, vector NewAccel, eDoubleClickDir DoubleClickMove, rotator DeltaRot)
    {
        local vector OldAccel;

        OldAccel = Pawn.Acceleration;
        if ( Pawn.Acceleration != NewAccel )
			Pawn.Acceleration = NewAccel;

        if ( bPressedJump )
            Pawn.DoJump(bUpdating);
    }

    function PlayerMove( float DeltaTime )
    {
        local vector NewAccel;
        local eDoubleClickDir DoubleClickMove;
        local rotator OldRotation, ViewRotation;
        local bool  bSaveJump;

        GroundPitch = 0;
        ViewRotation = Rotation;

        Pawn.CheckBob(DeltaTime,vect(0,0,0));

        // Update rotation.
        SetRotation(ViewRotation);
        OldRotation = Rotation;
        UpdateRotation(DeltaTime, 1);

        // Update acceleration.
        NewAccel = aForward*Normal(ViewX - OldFloor * (OldFloor Dot ViewX)) + aStrafe*ViewY;
        if ( VSize(NewAccel) < 1.0 )
            NewAccel = vect(0,0,0);

        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;
    }

    function BeginState()
    {
        if ( Pawn.Mesh == None )
            Pawn.SetMesh();
        OldFloor = vect(0,0,1);
        GetAxes(Rotation,ViewX,ViewY,ViewZ);
        DoubleClickDir = DCLICK_None;
        Pawn.ShouldCrouch(false);
        Pawn.ShouldProne(false);
        bPressedJump = false;
        if (Pawn.Physics != PHYS_Falling)
            Pawn.SetPhysics(PHYS_Spider);
        GroundPitch = 0;
        Pawn.bCrawler = true;
        Pawn.SetCollisionSize(Pawn.Default.CollisionHeight,Pawn.Default.CollisionHeight);
    }

    function EndState()
    {
        GroundPitch = 0;
        if ( Pawn != None )
        {
            Pawn.SetCollisionSize(Pawn.Default.CollisionRadius,Pawn.Default.CollisionHeight);
            Pawn.ShouldCrouch(false);
            Pawn.ShouldProne(false);
            Pawn.bCrawler = Pawn.Default.bCrawler;
        }
    }
}

// Player movement.
// Player Swimming
state PlayerSwimming
{
ignores SeePlayer, HearNoise, Bump;

    function bool WantsSmoothedView()
    {
        return ( !Pawn.bJustLanded );
    }

    function bool NotifyLanded(vector HitNormal)
    {
        if ( Pawn.PhysicsVolume.bWaterVolume )
            Pawn.SetPhysics(PHYS_Swimming);
        else
            GotoState(Pawn.LandMovementState);
        return bUpdating;
    }

    function bool NotifyPhysicsVolumeChange( PhysicsVolume NewVolume )
    {
        local actor HitActor;
        local vector HitLocation, HitNormal, checkpoint;

        if ( !NewVolume.bWaterVolume )
        {
            Pawn.SetPhysics(PHYS_Falling);
            if ( Pawn.Velocity.Z > 0 )
            {
				if (Pawn.bUpAndOut && Pawn.CheckWaterJump(HitNormal)) //check for waterjump
				{
					Pawn.velocity.Z = FMax(Pawn.JumpZ,420) + 2 * Pawn.CollisionRadius; //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.CollisionHeight + 6.0);
					HitActor = Trace(HitLocation, HitNormal, checkpoint, Pawn.Location, false);
					if (HitActor != None)
						GotoState(Pawn.LandMovementState);
					else
					{
						Enable('Timer');
						SetTimer(0.7,false);
					}
				}
			}
        }
        else
        {
            Disable('Timer');
            Pawn.SetPhysics(PHYS_Swimming);
        }
        return false;
    }

    function ProcessMove(float DeltaTime, vector NewAccel, eDoubleClickDir DoubleClickMove, rotator DeltaRot)
    {
        local vector X,Y,Z, OldAccel;
		local bool bUpAndOut;

        GetAxes(Rotation,X,Y,Z);
        OldAccel = Pawn.Acceleration;
        if ( Pawn.Acceleration != NewAccel )
			Pawn.Acceleration = NewAccel;
        bUpAndOut = ((X Dot Pawn.Acceleration) > 0) && ((Pawn.Acceleration.Z > 0) || (Rotation.Pitch > 2048));
		if ( Pawn.bUpAndOut != bUpAndOut )
			Pawn.bUpAndOut = bUpAndOut;
        if ( !Pawn.PhysicsVolume.bWaterVolume ) //check for waterjump
            NotifyPhysicsVolumeChange(Pawn.PhysicsVolume);
    }

    function PlayerMove(float DeltaTime)
    {
        local rotator oldRotation;
        local vector X,Y,Z, NewAccel;

        GetAxes(Rotation,X,Y,Z);

        NewAccel = aForward*X + aStrafe*Y + aUp*vect(0,0,1);
        if ( VSize(NewAccel) < 1.0 )
            NewAccel = vect(0,0,0);

        //add bobbing when swimming
        Pawn.CheckBob(DeltaTime, Y);

        // Update rotation.
        oldRotation = Rotation;
        UpdateRotation(DeltaTime, 2);

        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;
    }

    function Timer()
    {
        if ( !Pawn.PhysicsVolume.bWaterVolume && (Role == ROLE_Authority) )
            GotoState(Pawn.LandMovementState);

        Disable('Timer');
    }

    function BeginState()
    {
        Disable('Timer');
        Pawn.SetPhysics(PHYS_Swimming);

        // This ensures we don't get stuck viewing someone else when we respawn
// if _RO_
	    if( Role==ROLE_Authority )
	    {
		    if( Pawn!=none )
		    {
				SetViewTarget(Pawn);
				ClientSetViewTarget(Pawn);
			}
			else
			{
				SetViewTarget(self);
				ClientSetViewTarget(self);
			}

		    ClientSetBehindView(false);
	    }
// end RO
    }
}

state PlayerFlying
{
ignores SeePlayer, HearNoise, Bump;

    function PlayerMove(float DeltaTime)
    {
        local vector X,Y,Z;

        GetAxes(Rotation,X,Y,Z);

        Pawn.Acceleration = aForward*X + aStrafe*Y;
        if ( VSize(Pawn.Acceleration) < 1.0 )
            Pawn.Acceleration = vect(0,0,0);
        if ( bCheatFlying && (Pawn.Acceleration == vect(0,0,0)) )
            Pawn.Velocity = vect(0,0,0);
        // Update rotation.
        UpdateRotation(DeltaTime, 2);

        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));
    }

    function BeginState()
    {
        Pawn.SetPhysics(PHYS_Flying);
    }
}

state PlayerSpaceFlying
{
ignores SeePlayer, HearNoise, Bump;

	function CallServerMove
	(
	    float TimeStamp,
	    vector InAccel,
	    vector ClientLoc,
	    bool NewbRun,
	    bool NewbDuck,
	    bool NewbPendingJumpStatus,
	    bool NewbJumpStatus,
	    bool NewbSprint,
		bool NewbCrawl,
	    eDoubleClickDir DoubleClickMove,
	    byte ClientRoll,
	    int View,
	    optional int FreeAimRot,
	    optional byte OldTimeDelta,
	    optional int OldAccel
	)
	{
		local Rotator ViewRot;

		if ( Pawn == None )
			ViewRot = Rotation;
		else
			ViewRot = Pawn.Rotation;

		if ( PendingMove != None )
		{
			DualSpaceFighterServerMove
			(
				PendingMove.TimeStamp,
		        PendingMove.Acceleration * 10,
				PendingMove.bDuck,
				PendingMove.Rotation.Pitch,
				PendingMove.Rotation.Yaw,
				PendingMove.Rotation.Roll,
				TimeStamp,
				InAccel,
				ClientLoc,
				NewbDuck,
				ViewRot.Pitch,
				ViewRot.Yaw,
				ViewRot.Roll
			);
		}
		else
			SpaceFighterServerMove
			(
				TimeStamp,
				InAccel,
				ClientLoc,
				NewbDuck,
				ViewRot.Pitch,
				ViewRot.Yaw,
				ViewRot.Roll
			);
	}

	/* ServerMove()
	- replicated function sent by client to server - contains client movement and firing info
	Passes acceleration in components so it doesn't get rounded.
	IGNORE VANILLA SERVER MOVES
	*/
	function ServerMove
	(
	    float TimeStamp,
	    vector InAccel,
	    vector ClientLoc,
		byte NewActions,
	    eDoubleClickDir DoubleClickMove,
	    byte ClientRoll,
	    int View,
	    optional int FreeAimRot,
	    optional byte OldTimeDelta,
	    optional int OldAccel
	)
	{
		// If this move is outdated, discard it.
		if ( CurrentTimeStamp >= TimeStamp )
			return;

		if ( AcknowledgedPawn != Pawn )
		{
			OldTimeDelta = 0;
			InAccel = vect(0,0,0);
		}
		else
			Pawn.AutonomousPhysics(TimeStamp - CurrentTimeStamp);

		CurrentTimeStamp	= TimeStamp;
		ServerTimeStamp		= Level.TimeSeconds;
	}

    function PlayerMove(float DeltaTime)
    {
		if ( Pawn == None )
		{
			GotoState('dead');
			return;
		}

		Pawn.UpdateRocketAcceleration(DeltaTime, aTurn, aLookUp);
		SetRotation( Pawn.Rotation );

		ViewShake( DeltaTime );
        ViewFlash( DeltaTime );

        if ( Role < ROLE_Authority ) // then save this move and replicate it
			ReplicateMove(DeltaTime, Pawn.Acceleration, DCLICK_None, Pawn.Rotation );
        else
            ProcessMove(DeltaTime, Pawn.Acceleration, DCLICK_None, rot(0,0,0));
    }

	function ProcessMove(float DeltaTime, vector NewAccel, eDoubleClickDir DoubleClickMove, rotator DeltaRot)
    {
		if ( Pawn == None )
			return;

        if ( Pawn.Acceleration != NewAccel )
			Pawn.Acceleration = NewAccel;

		if ( DeltaRot != rot(0,0,0) )
		{
			if ( Pawn.Rotation != DeltaRot )
				Pawn.SetRotation( DeltaRot );
			if ( Rotation != DeltaRot )
				SetRotation( DeltaRot );
		}

		Pawn.Velocity = Pawn.Acceleration * Pawn.AirSpeed * 0.001;
		Pawn.ProcessMove(DeltaTime, NewAccel, DoubleClickMove, DeltaRot);
	}

    function BeginState()
    {
		if ( Pawn != None )
			Pawn.SetPhysics( PHYS_Flying );

		RotationRate.Pitch	= 8192; // extending pitch limit

		bIsSpaceFighter = true;
    }

    function EndState()
    {
		RotationRate.Pitch = default.RotationRate.Pitch; // restoring pitch limit
		bIsSpaceFighter = false;
	}
}


state PlayerRocketing
{
ignores SeePlayer, HearNoise, Bump;

	function CallServerMove
	(
	    float TimeStamp,
	    vector InAccel,
	    vector ClientLoc,
	    bool NewbRun,
	    bool NewbDuck,
	    bool NewbPendingJumpStatus,
	    bool NewbJumpStatus,
	    bool NewbSprint,
		bool NewbCrawl,
	    eDoubleClickDir DoubleClickMove,
	    byte ClientRoll,
	    int View,
	    optional int FreeAimRot,
	    optional byte OldTimeDelta,
	    optional int OldAccel
	)
	{
		if ( PendingMove != None )
		{
			DualRocketServerMove
			(
				PendingMove.TimeStamp,
		        PendingMove.Acceleration * 10,
				PendingMove.bDuck,
		        ((PendingMove.Rotation.Roll >> 8) & 255),
				(32767 & (PendingMove.Rotation.Pitch/2)) * 32768 + (32767 & (PendingMove.Rotation.Yaw/2)),
				TimeStamp,
				InAccel,
				ClientLoc,
				NewbDuck,
				ClientRoll,
				View
			);
		}
		else
		{
			RocketServerMove
			(
				TimeStamp,
				InAccel,
				ClientLoc,
				NewbDuck,
				ClientRoll,
				View
			);
		}
	}

	/* ServerMove()
	- replicated function sent by client to server - contains client movement and firing info
	Passes acceleration in components so it doesn't get rounded.
	IGNORE VANILLA SERVER MOVES
	*/
	function ServerMove
	(
	    float TimeStamp,
	    vector InAccel,
	    vector ClientLoc,
		byte NewActions,
	    eDoubleClickDir DoubleClickMove,
	    byte ClientRoll,
	    int View,
	    optional int FreeAimRot,
	    optional byte OldTimeDelta,
	    optional int OldAccel
	)
	{
		// If this move is outdated, discard it.
		if ( CurrentTimeStamp >= TimeStamp )
			return;

		if ( !CheckSpeedHack(TimeStamp - CurrentTimeStamp) )
		{
			if ( !bWasSpeedHack )
			{
				if ( Level.TimeSeconds - LastSpeedHackLog > 20 )
				{
					log("Possible speed hack by "$PlayerReplicationInfo.PlayerName);
					LastSpeedHackLog = Level.TimeSeconds;
				}
				ClientMessage( "Speed Hack Detected!",'CriticalEvent' );
			}
			else
				bWasSpeedHack = true;
		}
		else
		{
			if ( AcknowledgedPawn != Pawn )
			{
				OldTimeDelta = 0;
				InAccel = vect(0,0,0);
			}
			else
				Pawn.AutonomousPhysics(TimeStamp - CurrentTimeStamp);
		}
		CurrentTimeStamp	= TimeStamp;
		ServerTimeStamp		= Level.TimeSeconds;
	}

	function RocketServerMove
	(
		float	TimeStamp,
		vector	InAccel,
		vector	ClientLoc,
		bool	NewbDuck,
		byte	ClientRoll,
		int		View
	)
	{
		local byte NewActions;

		// Put together new actions to send (compressed into one byte)
		if (NewbDuck)
			NewActions += 2;

		Global.ServerMove(TimeStamp,InAccel,ClientLoc,NewActions, DCLICK_NONE,ClientRoll,View);
	}

	function DualRocketServerMove
	(
		float	TimeStamp0,
		vector	InAccel0,
		bool	NewbDuck0,
		byte	ClientRoll0,
		int		View0,
		float	TimeStamp,
		vector	InAccel,
		vector	ClientLoc,
		bool	NewbDuck,
		byte	ClientRoll,
		int		View
	)
	{
		local byte NewActions, NewActions0;

		// Put together new actions to send (compressed into one byte)
		if (NewbDuck)
			NewActions += 2;

		if (NewbDuck0)
			NewActions0 += 2;

		Global.ServerMove(TimeStamp0,InAccel0,vect(0,0,0),NewActions0, DCLICK_NONE,ClientRoll0,View0);
		Global.ServerMove(TimeStamp,InAccel,ClientLoc,NewActions, DCLICK_NONE,ClientRoll,View);
	}

    function PlayerMove(float DeltaTime)
    {
		Pawn.UpdateRocketAcceleration(DeltaTime,aTurn,aLookUp);
		SetRotation(Pawn.Rotation);

        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));
    }

	function ProcessMove(float DeltaTime, vector NewAccel, eDoubleClickDir DoubleClickMove, rotator DeltaRot)
    {
		if ( Pawn == None )
			return;

        if ( Pawn.Acceleration != NewAccel )
			Pawn.Acceleration = NewAccel;
	}

    function BeginState()
    {
		if ( Pawn != None )
			Pawn.SetPhysics( PHYS_Flying );

		RotationRate.Pitch	= 8192; // extending pitch limit (limits network weapon aiming)
    }

    function EndState()
    {
		RotationRate.Pitch = default.RotationRate.Pitch; // restoring pitch limit
	}
}


state PlayerTurreting
{
ignores SeePlayer, HearNoise, Bump;

	function CallServerMove
	(
	    float TimeStamp,
	    vector InAccel,
	    vector ClientLoc,
	    bool NewbRun,
	    bool NewbDuck,
	    bool NewbPendingJumpStatus,
	    bool NewbJumpStatus,
	    bool NewbSprint,
		bool NewbCrawl,
	    eDoubleClickDir DoubleClickMove,
	    byte ClientRoll,
	    int View,
	    optional int FreeAimRot,
	    optional byte OldTimeDelta,
	    optional int OldAccel
	)
	{

		if ( PendingMove != None )
		{
			DualTurretServerMove
			(
				PendingMove.TimeStamp,
				PendingMove.bDuck,
		        ((PendingMove.Rotation.Roll >> 8) & 255),
				(32767 & (PendingMove.Rotation.Pitch/2)) * 32768 + (32767 & (PendingMove.Rotation.Yaw/2)),
				TimeStamp,
				ClientLoc,
				NewbDuck,
				ClientRoll,
				View
			);
		}
		else
			TurretServerMove
			(
				TimeStamp,
				ClientLoc,
				NewbDuck,
				ClientRoll,
				View
			);
	}

	/* ServerMove()
	- replicated function sent by client to server - contains client movement and firing info
	Passes acceleration in components so it doesn't get rounded.
	IGNORE VANILLA SERVER MOVES
	*/
	function ServerMove
	(
	    float TimeStamp,
	    vector InAccel,
	    vector ClientLoc,
		byte NewActions,
	    eDoubleClickDir DoubleClickMove,
	    byte ClientRoll,
	    int View,
	    optional int FreeAimRot,
	    optional byte OldTimeDelta,
	    optional int OldAccel
	)
	{
		// If this move is outdated, discard it.
		if ( CurrentTimeStamp >= TimeStamp )
			return;

		if ( AcknowledgedPawn != Pawn )
		{
			OldTimeDelta = 0;
			InAccel = vect(0,0,0);
		}

		if ( AcknowledgedPawn == Pawn && CurrentTimeStamp < TimeStamp )
	       Pawn.AutonomousPhysics(TimeStamp - CurrentTimeStamp);
		CurrentTimeStamp = TimeStamp;
		ServerTimeStamp = Level.TimeSeconds;
	}

	function TurretServerMove
	(
		float	TimeStamp,
		vector	ClientLoc,
		bool	NewbDuck,
		byte	ClientRoll,
		int		View,
		optional int FreeAimRot
	)
	{
		local byte NewActions;

		// Put together new actions to send (compressed into one byte)
		if (NewbDuck)
			NewActions += 2;

		Global.ServerMove(TimeStamp,Vect(0,0,0),ClientLoc,NewActions, DCLICK_NONE,ClientRoll,View,FreeAimRot);
	}

	/* DualTurretServerMove()
	compressed version of server move for PlayerTurreting state
	*/
	function DualTurretServerMove
	(
		float	TimeStamp0,
		bool	NewbDuck0,
		byte	ClientRoll0,
		int		View0,
		float	TimeStamp,
		vector	ClientLoc,
		bool	NewbDuck,
		byte	ClientRoll,
		int		View,
		optional int FreeAimRot
	)
	{
		local byte NewActions, NewActions0;

		// Put together new actions to send (compressed into one byte)
		if (NewbDuck)
			NewActions += 2;

		if (NewbDuck0)
			NewActions0 += 2;

		Global.ServerMove(TimeStamp0,Vect(0,0,0),vect(0,0,0),NewActions0, DCLICK_NONE,ClientRoll0,View0,FreeAimRot);
		Global.ServerMove(TimeStamp,Vect(0,0,0),ClientLoc,NewActions, DCLICK_NONE,ClientRoll,View,FreeAimRot);
	}

    function PlayerMove(float DeltaTime)
    {
        local Vehicle CurrentVehicle;

		if ( Pawn == None )
		{
			GotoState('dead');
			return;
		}

		Pawn.UpdateRocketAcceleration(DeltaTime, aTurn, aLookUp);
		if ( !bFreeCamera )
			SetRotation( Pawn.Rotation );

		ViewShake( deltaTime );
        ViewFlash( 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));

		CurrentVehicle = Vehicle(Pawn);
		if ( CurrentVehicle != None )
		{
			if ( bFire == 0 && CurrentVehicle.bWeaponIsFiring )
				CurrentVehicle.ClientVehicleCeaseFire( false );

			if ( bAltFire == 0 && CurrentVehicle.bWeaponIsAltFiring )
				CurrentVehicle.ClientVehicleCeaseFire( true );
		}
    }

	function ProcessMove(float DeltaTime, vector NewAccel, eDoubleClickDir DoubleClickMove, rotator DeltaRot)
    {
		if ( Pawn == None )
			return;

		Pawn.Acceleration = newAccel;
	}

    function BeginState()
    {
		if ( Pawn != None )
			Pawn.SetPhysics( PHYS_Flying );

		RotationRate.Pitch	= 16384; // extending pitch limit (limits network weapon aiming)
    }

    function EndState()
    {
		RotationRate.Pitch = default.RotationRate.Pitch; // restoring pitch limit
	}

Begin:
}

function bool IsSpectating()
{
	return false;
}

state BaseSpectating
{
	function bool IsSpectating()
	{
		return true;
	}

    function ProcessMove(float DeltaTime, vector NewAccel, eDoubleClickDir DoubleClickMove, rotator DeltaRot)
    {
        Acceleration = NewAccel;
        MoveSmooth(SpectateSpeed * Normal(Acceleration) * DeltaTime);
    }

    function PlayerMove(float DeltaTime)
    {
        local vector X,Y,Z;

		if ( (Pawn(ViewTarget) != None) && (Level.NetMode == NM_Client) )
		{
			if ( Pawn(ViewTarget).bSimulateGravity )
				TargetViewRotation.Roll = 0;
			BlendedTargetViewRotation.Pitch = BlendRot(DeltaTime, BlendedTargetViewRotation.Pitch, TargetViewRotation.Pitch & 65535);
			BlendedTargetViewRotation.Yaw = BlendRot(DeltaTime, BlendedTargetViewRotation.Yaw, TargetViewRotation.Yaw & 65535);
			BlendedTargetViewRotation.Roll = BlendRot(DeltaTime, BlendedTargetViewRotation.Roll, TargetViewRotation.Roll & 65535);
		}
        GetAxes(Rotation,X,Y,Z);

        Acceleration = 0.02 * (aForward*X + aStrafe*Y + aUp*vect(0,0,1));

        UpdateRotation(DeltaTime, 1);

        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));
    }
}

state Scripting
{
    // FIXME - IF HIT FIRE, AND NOT bInterpolating, Leave script
    exec function Fire( optional float F )
    {
    }

    exec function AltFire( optional float F )
    {
        Fire(F);
    }
}

function ServerViewNextPlayer()
{
    local Controller C, Pick;
    local bool bFound, bRealSpec, bWasSpec;
	local TeamInfo RealTeam;

	if( !IsInState('Spectating') )
	{
	    return;
	}

    bRealSpec = PlayerReplicationInfo.bOnlySpectator;
    bWasSpec = !bBehindView && (ViewTarget != Pawn) && (ViewTarget != self);
    PlayerReplicationInfo.bOnlySpectator = true;
    RealTeam = PlayerReplicationInfo.Team;

    // view next player
    for ( C=Level.ControllerList; C!=None; C=C.NextController )
    {
		if ( bRealSpec && (C.PlayerReplicationInfo != None) ) // hack fix for invasion spectating
			PlayerReplicationInfo.Team = C.PlayerReplicationInfo.Team;
        if ( Level.Game.CanSpectate(self,bRealSpec,C) )
        {
            if ( Pick == None )
                Pick = C;
            if ( bFound )
            {
                Pick = C;
                break;
            }
            else
                bFound = ( (RealViewTarget == C) || (ViewTarget == C) );
        }
    }
    PlayerReplicationInfo.Team = RealTeam;
    SetViewTarget(Pick);
    ClientSetViewTarget(Pick);
    if ( (ViewTarget == self) || bWasSpec )
        bBehindView = false;
    else
        bBehindView = true; //bChaseCam;
    ClientSetBehindView(bBehindView);
    PlayerReplicationInfo.bOnlySpectator = bRealSpec;
}

function ServerViewSelf()
{
	SetLocation(ViewTarget.Location);
	ClientSetLocation(ViewTarget.Location, Rotation);

    bBehindView = false;
    SetViewTarget(self);
    ClientSetViewTarget(self);
    ClientMessage(OwnCamera, 'Event');
}

function LoadPlayers()
{
	local int i;

	if ( GameReplicationInfo == None )
		return;

	for ( i=0; i<GameReplicationInfo.PRIArray.Length; i++ )
		GameReplicationInfo.PRIArray[i].UpdatePrecacheMaterials();
}

function ServerSpectate()
{
	// Proper fix for phantom pawns

	if (Pawn != none && !Pawn.bDeleteMe)
	{
		Pawn.Died(self, class'DamageType', Pawn.Location);
	}

	GotoState('Spectating');
	bBehindView = true;
	ServerViewNextPlayer();
}

//active player wants to become a spectator
function BecomeSpectator()
{
	if (Role < ROLE_Authority)
		return;

	if ( !Level.Game.BecomeSpectator(self) )
		return;

	if ( Pawn != None )
		Pawn.Died(self, class'DamageType', Pawn.Location);

	if ( PlayerReplicationInfo.Team != None )
		PlayerReplicationInfo.Team.RemoveFromTeam(self);
	PlayerReplicationInfo.Team = None;
	PlayerReplicationInfo.Score = 0;
	PlayerReplicationInfo.Deaths = 0;
	PlayerReplicationInfo.GoalsScored = 0;
	PlayerReplicationInfo.Kills = 0;
	ServerSpectate();
// if _RO_
	BroadcastLocalizedMessage(class'GameMessage', 14, PlayerReplicationInfo);
// else
//  BroadcastLocalizedMessage(Level.Game.GameMessageClass, 14, PlayerReplicationInfo);
// end if _RO_

	ClientBecameSpectator();
}

function ClientBecameSpectator()
{
// if _RO_
	UpdateURL("SpectatorOnly", "1", false);
// else
//	UpdateURL("SpectatorOnly", "1", true);
// end if _RO_
}

//spectating player wants to become active and join the game
function BecomeActivePlayer()
{
	if (Role < ROLE_Authority)
		return;

	if ( !Level.Game.AllowBecomeActivePlayer(self) )
		return;

	bBehindView = false;
	FixFOV();
	SetViewDistance();
	ServerViewSelf();
	PlayerReplicationInfo.bOnlySpectator = false;
	Level.Game.NumSpectators--;
	Level.Game.NumPlayers++;
	PlayerReplicationInfo.Reset();
	Adrenaline = 0;
	BroadcastLocalizedMessage(Level.Game.GameMessageClass, 1, PlayerReplicationInfo);
	if (Level.Game.bTeamGame)
		Level.Game.ChangeTeam(self, Level.Game.PickTeam(int(GetURLOption("Team")), None), false);
	if (!Level.Game.bDelayedStart)
    {
		// start match, or let player enter, immediately
		Level.Game.bRestartLevel = false;  // let player spawn once in levels that must be restarted after every death
		if (Level.Game.bWaitingToStartMatch)
			Level.Game.StartMatch();
		else
			Level.Game.RestartPlayer(PlayerController(Owner));
		Level.Game.bRestartLevel = Level.Game.Default.bRestartLevel;
    }
    else
        GotoState('PlayerWaiting');

    ClientBecameActivePlayer();
}

function ClientBecameActivePlayer()
{
	UpdateURL("SpectatorOnly","",true);
}

state Spectating extends BaseSpectating
{
    ignores SwitchWeapon, RestartLevel, ClientRestart, Suicide,
     ThrowWeapon, NotifyPhysicsVolumeChange, NotifyHeadVolumeChange;

    exec function Fire( optional float F )
    {
    	if ( bFrozen )
	{
		if ( (TimerRate <= 0.0) || (TimerRate > 1.0) )
			bFrozen = false;
		return;
	}

        ServerViewNextPlayer();
    }

    // Return to spectator's own camera.
    exec function AltFire( optional float F )
    {
        bBehindView = false;
        ServerViewSelf();
    }

    function Timer()
    {
    	bFrozen = false;
    }

    function BeginState()
    {
        if ( Pawn != None )
        {
            SetLocation(Pawn.Location);
            UnPossess();
        }
        bCollideWorld = true;
		CameraDist = Default.CameraDist;
    }

    function EndState()
    {
        PlayerReplicationInfo.bIsSpectator = false;
        bCollideWorld = false;
    }
}

state AttractMode extends Spectating
{
}

auto state PlayerWaiting extends BaseSpectating
{
ignores SeePlayer, HearNoise, NotifyBump, TakeDamage, PhysicsVolumeChange, NextWeapon, PrevWeapon, SwitchToBestWeapon;

    exec function Jump( optional float F )
    {
    }

    exec function Suicide()
    {
    }

    function ServerRestartPlayer()
    {
        if ( Level.TimeSeconds < WaitDelay )
            return;
        if ( Level.NetMode == NM_Client )
            return;
        if ( Level.Game.bWaitingToStartMatch )
            PlayerReplicationInfo.bReadyToPlay = true;
        else
            Level.Game.RestartPlayer(self);
        }

    exec function Fire(optional float F)
    {
        LoadPlayers();
        if ( !bForcePrecache && (Level.TimeSeconds > 0.2) )
			ServerReStartPlayer();
    }

    exec function AltFire(optional float F)
    {
        Fire(F);
    }

    function EndState()
    {
        if ( Pawn != None )
            Pawn.SetMesh();
        if ( PlayerReplicationInfo != None )
			PlayerReplicationInfo.SetWaitingPlayer(false);
        bCollideWorld = false;
    }

    function BeginState()
    {
		CameraDist = Default.CameraDist;
        if ( PlayerReplicationInfo != None )
            PlayerReplicationInfo.SetWaitingPlayer(true);
        bCollideWorld = true;
    }
}

state WaitingForPawn extends BaseSpectating
{
ignores SeePlayer, HearNoise, KilledBy, SwitchWeapon;

    exec function Fire( optional float F )
    {
		AskForPawn();
    }

    exec function AltFire( optional float F )
    {
    }

    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 == 'GameEnded' || newState == 'RoundEnded' )
			GotoState(newState);
    }

    function PlayerTick(float DeltaTime)
    {
        Global.PlayerTick(DeltaTime);

        if ( Pawn != None )
        {
            Pawn.Controller = self;
            Pawn.bUpdateEyeHeight = true;
            //log("Client restart in waitingforpawn");
            ClientRestart(Pawn);
        }
        else if ( (TimerRate <= 0.0) || (TimerRate > 1.0) )
		{
			SetTimer(0.2,true);
			AskForPawn();
		}
    }

    function Timer()
    {
        AskForPawn();
    }

    function BeginState()
    {
        SetTimer(0.2, true);
        AskForPawn();
    }

    function EndState()
    {
		bBehindView = false;
        SetTimer(0.0, false);
    }
}

state GameEnded
{
ignores SeePlayer, HearNoise, KilledBy, NotifyBump, HitWall, NotifyHeadVolumeChange, NotifyPhysicsVolumeChange, Falling, TakeDamage, Suicide;

	function ServerReStartPlayer()
	{
	}

	function bool IsSpectating()
	{
		return true;
	}

	exec function Use() {}
	exec function SwitchWeapon(byte T) {}
	exec function ThrowWeapon() {}

	function Possess(Pawn aPawn)
	{
		Global.Possess(aPawn);

		if (Pawn != None)
			Pawn.TurnOff();
	}

    function ServerReStartGame()
    {
		if ( Level.Game.PlayerCanRestartGame( Self ) )
			Level.Game.RestartGame();
    }

    exec function Fire( optional float F )
    {
        if ( Role < ROLE_Authority)
            return;
        if ( !bFrozen )
            ServerReStartGame();
        else if ( TimerRate <= 0 )
            SetTimer(1.5, false);
    }

    exec function AltFire( optional float F )
    {
        Fire(F);
    }

    function PlayerMove(float DeltaTime)
    {
        local vector X,Y,Z;
        local Rotator ViewRotation;

        GetAxes(Rotation,X,Y,Z);
        // Update view rotation.

        if ( !bFixedCamera )
        {
            ViewRotation = Rotation;
            ViewRotation.Yaw += 32.0 * DeltaTime * aTurn;
            ViewRotation.Pitch += 32.0 * DeltaTime * aLookUp;
            if (Pawn != None)
	            ViewRotation.Pitch = Pawn.LimitPitch(ViewRotation.Pitch);
            SetRotation(ViewRotation);
        }
        else if ( ViewTarget != None )
            SetRotation(ViewTarget.Rotation);

        ViewShake(DeltaTime);
        ViewFlash(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;
    }

	function ServerMove
	(
	    float TimeStamp,
	    vector InAccel,
	    vector ClientLoc,
		byte NewActions,
	    eDoubleClickDir DoubleClickMove,
	    byte ClientRoll,
	    int View,
	    optional int FreeAimRot,
	    optional byte OldTimeDelta,
	    optional int OldAccel
	)
    {
        Global.ServerMove(TimeStamp, InAccel, ClientLoc, NewActions,
                            DoubleClickMove, ClientRoll, (32767 & (Rotation.Pitch/2)) * 32768 + (32767 & (Rotation.Yaw/2)) );

    }

    function FindGoodView()
    {
        local vector cameraLoc;
        local rotator cameraRot, ViewRotation;
        local int tries, besttry;
        local float bestdist, newdist;
        local int startYaw;
        local actor ViewActor;

        ViewRotation = Rotation;
        ViewRotation.Pitch = 56000;
        tries = 0;
        besttry = 0;
        bestdist = 0.0;
        startYaw = ViewRotation.Yaw;

        for (tries=0; tries<16; tries++)
        {
            cameraLoc = ViewTarget.Location;
			SetRotation(ViewRotation);
            PlayerCalcView(ViewActor, cameraLoc, cameraRot);
            newdist = VSize(cameraLoc - ViewTarget.Location);
            if (newdist > bestdist)
            {
                bestdist = newdist;
                besttry = tries;
            }
            ViewRotation.Yaw += 4096;
        }

        ViewRotation.Yaw = startYaw + besttry * 4096;
        SetRotation(ViewRotation);
    }

    function Timer()
    {
        bFrozen = false;
    }

    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
    )
    {
    }

    function BeginState()
    {
        local Pawn P;

        EndZoom();
		StopForceFeedback();
		CameraDist = Default.CameraDist;
        FOVAngle = DesiredFOV;
        bFire = 0;
        bAltFire = 0;
        if ( Pawn != None )
        {
       	    if ( Vehicle(Pawn) != None )
	    	Pawn.StopWeaponFiring();
			Pawn.TurnOff();
			Pawn.bSpecialHUD = false;
            Pawn.SimAnim.AnimRate = 0;
            if ( Pawn.Weapon != None )
            {
				Pawn.Weapon.StopFire(0);
				Pawn.Weapon.StopFire(1);
				Pawn.Weapon.bEndOfRound = true;
			}
        }
        bFrozen = true;
        if ( !bFixedCamera )
        {
            FindGoodView();
            bBehindView = true;
        }
        SetTimer(5, false);
        ForEach DynamicActors(class'Pawn', P)
        {
			if ( P.Role == ROLE_Authority )
				P.RemoteRole = ROLE_DumbProxy;
			P.TurnOff();
        }
    }

Begin:
}


state RoundEnded
{
ignores SeePlayer, HearNoise, KilledBy, NotifyBump, HitWall, NotifyHeadVolumeChange, NotifyPhysicsVolumeChange, Falling, TakeDamage, Suicide;

	function ServerReStartPlayer()
	{
	}

	function bool IsSpectating() {	return true; }

    exec function Use() {}
    exec function SwitchWeapon(byte T) {}
    exec function ThrowWeapon() {}
    exec function Fire(optional float F) {}
    exec function AltFire(optional float F) {}

    function Possess(Pawn aPawn)
    {
    	Global.Possess(aPawn);

    	if (Pawn != None)
    		Pawn.TurnOff();
    }

    function PlayerMove(float DeltaTime)
    {
        local vector X,Y,Z;
        local Rotator ViewRotation;

        GetAxes(Rotation,X,Y,Z);
        // Update view rotation.

        if ( !bFixedCamera )
        {
            ViewRotation = Rotation;
            ViewRotation.Yaw += 32.0 * DeltaTime * aTurn;
            ViewRotation.Pitch += 32.0 * DeltaTime * aLookUp;
            if (Pawn != None)
	            ViewRotation.Pitch = Pawn.LimitPitch(ViewRotation.Pitch);
            SetRotation(ViewRotation);
        }
        else if ( ViewTarget != None )
            SetRotation( ViewTarget.Rotation );

        ViewShake( DeltaTime );
        ViewFlash( 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;
    }

	function ServerMove
	(
	    float TimeStamp,
	    vector InAccel,
	    vector ClientLoc,
		byte NewActions,
	    eDoubleClickDir DoubleClickMove,
	    byte ClientRoll,
	    int View,
	    optional int FreeAimRot,
	    optional byte OldTimeDelta,
	    optional int OldAccel
	)
    {
        Global.ServerMove(TimeStamp, InAccel, ClientLoc, NewActions,
                            DoubleClickMove, ClientRoll, (32767 & (Rotation.Pitch/2)) * 32768 + (32767 & (Rotation.Yaw/2)) );

    }

    function FindGoodView()
    {
        local vector	cameraLoc;
        local rotator	cameraRot, ViewRotation;
        local int		tries, besttry;
        local float		bestdist, newdist;
        local int		startYaw;
        local actor		ViewActor;

        ViewRotation = Rotation;
        ViewRotation.Pitch = 56000;
        tries = 0;
        besttry = 0;
        bestdist = 0.0;
        startYaw = ViewRotation.Yaw;

        for (tries=0; tries<16; tries++)
        {
            cameraLoc = ViewTarget.Location;
			SetRotation(ViewRotation);
            PlayerCalcView(ViewActor, cameraLoc, cameraRot);
            newdist = VSize(cameraLoc - ViewTarget.Location);
            if ( newdist > bestdist )
            {
                bestdist = newdist;
                besttry = tries;
            }
            ViewRotation.Yaw += 4096;
        }

        ViewRotation.Yaw = startYaw + besttry * 4096;
        SetRotation(ViewRotation);
    }

    function Timer()
    {
        bFrozen = false;
    }

    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 == 'PlayerWaiting' )
			GotoState( newState );
    }

    function BeginState()
    {
        local Pawn P;

        EndZoom();
		CameraDist = Default.CameraDist;
        FOVAngle = DesiredFOV;
        bFire = 0;
        bAltFire = 0;

        if ( Pawn != None )
        {
       	    if ( Vehicle(Pawn) != None )
	    		Pawn.StopWeaponFiring();

			Pawn.TurnOff();
			Pawn.bSpecialHUD = false;
            Pawn.SimAnim.AnimRate = 0;
            if ( Pawn.Weapon != None )
			{
				Pawn.Weapon.StopFire(0);
				Pawn.Weapon.StopFire(1);
				Pawn.Weapon.bEndOfRound = true;
			}
        }

        bFrozen = true;
		bBehindView = true;
        if ( !bFixedCamera )
            FindGoodView();

        SetTimer(5, false);
        ForEach DynamicActors(class'Pawn', P)
        {
			if ( P.Role == ROLE_Authority )
				P.RemoteRole = ROLE_DumbProxy;
			P.TurnOff();
        }
		StopForceFeedback();
    }

	function CalcBehindView(out vector CameraLocation, out rotator CameraRotation, float Dist)
	{
		local vector	View;
		local float		ViewDist,RealDist;
		local vector	globalX,globalY,globalZ;
		local vector	localX,localY,localZ;
		local vector	HitLocation,HitNormal;
		local Actor		HitActor;

		CameraRotation = Rotation;
		CameraRotation.Roll = 0;
		CameraLocation.Z += 12;

		// add view rotation offset to cameraview (amb)
		CameraRotation += CameraDeltaRotation;

		View = vect(1,0,0) >> CameraRotation;

		// add view radius offset to camera location and move viewpoint up from origin (amb)
		RealDist = Dist;
		Dist += CameraDeltaRad;

		HitActor = Trace( HitLocation, HitNormal, CameraLocation - Dist * vector(CameraRotation), CameraLocation,false,vect(10,10,10));
		if ( HitActor != None && !HitActor.IsA('BlockingVolume') )
			ViewDist = FMin( (CameraLocation - HitLocation) Dot View, Dist );
		else
			ViewDist = Dist;

		if ( !bBlockCloseCamera || !bValidBehindCamera || (ViewDist > 10 + FMax(ViewTarget.CollisionRadius, ViewTarget.CollisionHeight)) )
		{
			//Log("Update Cam ");
			bValidBehindCamera = true;
			OldCameraLoc = CameraLocation - ViewDist * View;
			OldCameraRot = CameraRotation;
		}
		else
		{
			//Log("Dont Update Cam "$bBlockCloseCamera@bValidBehindCamera@ViewDist);
			SetRotation(OldCameraRot);
		}

		CameraLocation = OldCameraLoc;
		CameraRotation = OldCameraRot;

		// add view swivel rotation to cameraview (amb)
		GetAxes(CameraSwivel,globalX,globalY,globalZ);
		localX = globalX >> CameraRotation;
		localY = globalY >> CameraRotation;
		localZ = globalZ >> CameraRotation;
		CameraRotation = OrthoRotation(localX,localY,localZ);
	}

Begin:
}

state Dead
{
ignores SeePlayer, HearNoise, KilledBy, SwitchWeapon, NextWeapon, PrevWeapon;

	exec function ThrowWeapon()
	{
		//clientmessage("Throwweapon while dead, pawn "$Pawn$" health "$Pawn.health);
	}

	function bool IsDead()
	{
		return true;
	}

	function ServerReStartPlayer()
	{
		if ( !Level.Game.PlayerCanRestart( Self ) )
			return;

		super.ServerRestartPlayer();
	}

    exec function Fire( optional float F )
    {
		if ( bFrozen )
		{
			if ( (TimerRate <= 0.0) || (TimerRate > 1.0) )
				bFrozen = false;
			return;
		}

        LoadPlayers();

        if (bMenuBeforeRespawn)
        {
        	bMenuBeforeRespawn = false;
       		ShowMidGameMenu(false);
        }
        else
	        ServerReStartPlayer();
    }

    exec function AltFire( optional float F )
    {
    	Fire(F);
    }

    exec function Use()
    {
		if ( bFrozen )
		{
			if ( (TimerRate <= 0.0) || (TimerRate > 1.0) )
				bFrozen = false;
			return;
		}
		if ( Level.Game != None )
			Level.Game.DeadUse(self);
    }

	function ServerMove
	(
	    float TimeStamp,
	    vector InAccel,
	    vector ClientLoc,
		byte NewActions,
	    eDoubleClickDir DoubleClickMove,
	    byte ClientRoll,
	    int View,
	    optional int FreeAimRot,
	    optional byte OldTimeDelta,
	    optional int OldAccel
	)
    {
        Global.ServerMove(
                    TimeStamp,
                    InAccel,
                    ClientLoc,
                    0,
                    DoubleClickMove,
                    ClientRoll,
                    View);
    }

    function PlayerMove(float DeltaTime)
    {
        local vector X,Y,Z;
        local rotator ViewRotation;

        if ( !bFrozen )
        {
            if ( bPressedJump )
            {
                Fire(0);
                bPressedJump = false;
            }
            GetAxes(Rotation,X,Y,Z);
            // Update view rotation.
            ViewRotation = Rotation;
            ViewRotation.Yaw += 32.0 * DeltaTime * aTurn;
            ViewRotation.Pitch += 32.0 * DeltaTime * aLookUp;
            if (Pawn != None)
	            ViewRotation.Pitch = Pawn.LimitPitch(ViewRotation.Pitch);
            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 ( (TimerRate <= 0.0) || (TimerRate > 1.0) )
			bFrozen = false;

        ViewShake(DeltaTime);
        ViewFlash(DeltaTime);
    }

    function FindGoodView()
    {
        local vector cameraLoc;
        local rotator cameraRot, ViewRotation;
        local int tries, besttry;
        local float bestdist, newdist;
        local int startYaw;
        local actor ViewActor;

        ////log("Find good death scene view");
        ViewRotation = Rotation;
        ViewRotation.Pitch = 56000;
        tries = 0;
        besttry = 0;
        bestdist = 0.0;
        startYaw = ViewRotation.Yaw;

        for (tries=0; tries<16; tries++)
        {
            cameraLoc = ViewTarget.Location;
			SetRotation(ViewRotation);
            PlayerCalcView(ViewActor, cameraLoc, cameraRot);
            newdist = VSize(cameraLoc - ViewTarget.Location);
            if (newdist > bestdist)
            {
                bestdist = newdist;
                besttry = tries;
            }
            ViewRotation.Yaw += 4096;
        }

        ViewRotation.Yaw = startYaw + besttry * 4096;
        SetRotation(ViewRotation);
    }

    function Timer()
    {
        if (!bFrozen)
            return;

        bFrozen = false;
        bPressedJump = false;
    }

    function BeginState()
    {
		local Actor A;

		if ( (Pawn != None) && ((Pawn.Controller == self) || (Pawn.Controller == None)) )
			Pawn.Controller = None;
		EndZoom();
		CameraDist = Default.CameraDist;
		FOVAngle = DesiredFOV;
		Pawn = None;
        Enemy = None;
        bBehindView = true;
        bFrozen = true;
		bJumpStatus = false;
        bPressedJump = false;
        bBlockCloseCamera = true;
		bValidBehindCamera = false;
		bFreeCamera = False;
		if ( Viewport(Player) != None )
			ForEach DynamicActors(class'Actor',A)
				A.NotifyLocalPlayerDead(self);
        FindGoodView();
        SetTimer(1.0, false);
		StopForceFeedback();
		ClientPlayForceFeedback("Damage");  // jdf
		CleanOutSavedMoves();
    }

    function EndState()
    {
		StopForceFeedback();
		bBlockCloseCamera = false;
		CleanOutSavedMoves();
        Velocity = vect(0,0,0);
        Acceleration = vect(0,0,0);
        if ( !PlayerReplicationInfo.bOutOfLives )
			bBehindView = false;
        bPressedJump = false;
        if ( myHUD != None )
			myHUD.bShowScoreBoard = false;

		StopViewShaking();
    }

Begin:
    Sleep(3.0);
    if ( myHUD != None )
	    myHUD.bShowScoreBoard = true;
}

//------------------------------------------------------------------------------
// Control options
function ChangeStairLook( bool B )
{
    bLookUpStairs = B;
    if ( bLookUpStairs )
        bAlwaysMouseLook = false;
}

function ChangeAlwaysMouseLook(Bool B)
{
    bAlwaysMouseLook = B;
    if ( bAlwaysMouseLook )
        bLookUpStairs = false;
}

singular event UnPressButtons()
{
	bFire = 0;
	bAltFire = 0;
// if _RO_
	//bDuck = 0;
//
	bRun = 0;
	bVoiceTalk = 0;
	ResetInput();
}

// if _RO_
// Added this here so we can unpress these movement buttons when necessary (like the pawn died)
simulated function ClientResetMovement()
{
	bDuck = 0;
	bCrawl = 0;
}
// end _RO_
// Replace with good code
event ClientOpenMenu (string Menu, optional bool bDisconnect,optional string Msg1, optional string Msg2)
{
	// GUIController calls UnpressButtons() after it's been activated...once active, it swallows
	// all input events, preventing GameEngine from parsing script execs commands -- rjp
	if ( !Player.GUIController.OpenMenu(Menu, Msg1, Msg2) )
		UnPressButtons();

	if (bDisconnect)
	{
		// Use delayed console command, in case the menu that was opened had bDisconnectOnOpen=True -- rjp
		if ( Player.Console != None )
			Player.Console.DelayedConsoleCommand("DISCONNECT");
		else ConsoleCommand("Disconnect");
	}
}

event ClientReplaceMenu(string Menu, optional bool bDisconnect,optional string Msg1, optional string Msg2)
{
	if ( !Player.GUIController.bActive )
	{
		if ( !Player.GUIController.ReplaceMenu(Menu,Msg1,Msg2) )
			UnpressButtons();
	}
	else Player.GUIController.ReplaceMenu(Menu,Msg1,Msg2);

	if (bDisconnect)
	{
		if ( Player.Console != None )
			Player.Console.DelayedConsoleCommand("Disconnect");
		else ConsoleCommand("Disconnect");
	}
}

event ClientCloseMenu(optional bool bCloseAll, optional bool bCancel)
{
	if (bCloseAll)
		Player.GUIController.CloseAll(bCancel,True);
	else
		Player.GUIController.CloseMenu(bCancel);
}

event ClientNetworkMessage(string ParamA, string ParamB)
{
	ClientOpenMenu(Player.GUIController.NetworkMsgMenu, true, ParamA, ParamB);
}

simulated function bool IsMouseInverted()
{
	return PlayerInput.bInvertMouse;
}

exec function InvertMouse(optional string Invert)
{
	PlayerInput.InvertMouse(Invert);
}

exec function InvertLook()
{
    local bool result;

    result = PlayerInput.InvertLook();

    if (IsOnConsole())
    {
        class'XBoxPlayerInput'.default.bInvertVLook = result;
        class'XBoxPlayerInput'.static.StaticSaveConfig();
    }
}

function bool CanRestartPlayer()
{
    return !PlayerReplicationInfo.bOnlySpectator;
}

/////////////////////////////////////////////////////////////////////////////
// Admin Handling : Was previously in Admin.uc
//
//
//

// Execute an administrative console command on the server.
exec function Admin( string CommandLine )
{
	local string Result;

	if (AdminManager != None)
	{
		if ( Level.Game.AccessControl == None || !Level.Game.AccessControl.CanPerform(Self, "Xc") )
        	return;

		Result = ConsoleCommand( CommandLine );
		if (Level.Game.AccessControl.bReplyToGUI)
			AdminReply(Result);
		else if( Result != "" )
			ClientMessage( Result );
	}
}

exec function AdminDebug( string CommandLine )
{
	if ( AdminManager != None )
	{
		if ( Level.Game.AccessControl == None || !Level.Game.AccessControl.CanPerform(Self, "Xc") )
			return;

		ConsoleCommand(CommandLine, True);
	}
}

exec function AdminLogin( string CmdLine )
{
	if ( Level.TimeSeconds < NextLoginTime )
		return;

	NextLoginTime = Level.TimeSeconds + LoginDelay;
    ServerAdminLogin(CmdLine);
}

function ServerAdminLogin( string CmdLine )
{
	local string uname, upass;

	if (AdminManager == None)
	{
		MakeAdmin();
		if ( AdminManager != None )
		{
			if ( !Divide(CmdLine, " ", uname, upass) )
				upass = CmdLine;

			AdminManager.DoLogin(uname, upass);
			if ( !AdminManager.bAdmin )
				AdminManager = None;
			else AddCheats();
		}
	}
}

//if _RO_
exec function AdminLoginSilent( string CmdLine )
{
	if ( Level.TimeSeconds < NextLoginTime )
		return;

	NextLoginTime = Level.TimeSeconds + LoginDelay;
    ServerAdminLoginSilent(CmdLine);
}

function ServerAdminLoginSilent( string CmdLine )
{
	local string uname, upass;

	if (AdminManager == None)
	{
		MakeAdmin();
		if ( AdminManager != None )
		{
			if ( !Divide(CmdLine, " ", uname, upass) )
				upass = CmdLine;

			AdminManager.DoLoginSilent(uname, upass);

			if ( !AdminManager.bAdmin )
				AdminManager = None;
			else AddCheats();
		}
	}
}
//end _RO_

function AdminCommand( string CommandLine )
{
	if (Left(CommandLine, 11) ~= "AdminLogin ")
	{
		AdminLogin(Mid(CommandLine, 11));
		ReportAdmin("adv="$Level.Game.AccessControl.IsA('AccessControlIni'));
	}
	else if (Left(CommandLine, 11) ~= "AdminLogout")
	{
		AdminLogout();
		ReportAdmin();
	}
	else if (Level.Game.AccessControl != None)
	{
		Level.Game.AccessControl.bReplyToGUI = true;
		Admin(CommandLine);
		Level.Game.AccessControl.bReplyToGUI = false;
	}
}

function ReportAdmin(optional string ReportText)
{
	local string str;

	if (AdminManager != None && AdminManager.bAdmin)
	{
		if (Level.Game.AccessControl != None)
		{
			str = "name=" $ Level.Game.AccessControl.GetAdminName(Self);
			if ( ReportText != "" )
				str $= ";" $ ReportText;
		}
		else
		{
			str = "name=Admin";
			if ( ReportText != "" )
				str $= ";" $ ReportText;
		}
	}
	else str = ReportText;
	AdminReply(str);
}

function AdminReply( string Reply )
{
	if (Player.GUIController != None)
		Player.GUIController.OnAdminReply(Reply);
}

exec function AdminLogout()
{
  if (AdminManager != None)
  {
	AdminManager.DoLogout();
	if (!AdminManager.bAdmin)
		AdminManager = None;
  }
}

exec function AdminGUI()
{
}

/////////////////////////////////////////////////////////////////////////////
//
// Demo Recording
//
//


// Called on the client during client-side demo recording
simulated event StartClientDemoRec()
{
	// Here we replicate functions which the demo never saw.
	DemoClientSetHUD( MyHud.Class, MyHud.ScoreBoard.Class );

	// tell server to replicate more stuff to me
	bClientDemo = true;
	ServerSetClientDemo();
}

function ServerSetClientDemo()
{
	bClientDemo = true;
}

// Called on the playback client during client-side demo playback
simulated function DemoClientSetHUD(class<HUD> newHUDClass, class<Scoreboard> newScoringClass )
{
	if( MyHUD == None )
		ClientSetHUD( newHUDClass, newScoringClass );
}

simulated function string GetCustomStatusAnnouncerClass() { return ""; }
simulated function string GetCustomRewardAnnouncerClass() { return ""; }
simulated function SetCustomStatusAnnouncerClass(string NewAnnouncerClass);
simulated function SetCustomRewardAnnouncerClass(string NewAnnouncerClass);

simulated function bool NeedNetNotify()
{
	return false;
}

/////////////////////////////////////////////////////////////////////////////
//
// Voice Recognition
//
//

event VoiceCommand( string RecognizedString, string RawString )
{
	log(RecognizedString);
	TeamMessage( PlayerReplicationInfo, RawString, 'TeamSayQuiet' );
	if ( RecognizedString != "" )
		ServerVoiceCommand(RecognizedString);
}

exec function command(string RecognizedString)
{
	Level.Game.ParseVoiceCommand( self, RecognizedString );
}

function ServerVoiceCommand(string RecognizedString)
{
	Level.Game.ParseVoiceCommand( self, RecognizedString );
}

// =====================================================================================================================
// =====================================================================================================================
//  Voice Chat
// =====================================================================================================================
// =====================================================================================================================

// Join a voice chatroom by name
exec function Join(string ChanName, string ChanPwd)
{
	local int i, idx;
	local VoiceChatRoom VCR;

    //if _RO_
	if (VoiceReplicationInfo == None || !VoiceReplicationInfo.bEnableVoiceChat || !bVoiceChatEnabled)
		return;
	//else
	//if (VoiceReplicationInfo == none || !VoiceReplicationInfo.bEnableVoiceChat)
	//	return;
	//end _RO_

	for (i = 0; i < StoredChatPasswords.Length; i++)
	{
		if (ChanName ~= StoredChatPasswords[i].ChatRoomName)
		{
			if ( ChanPwd == "" )
				ChanPwd = StoredChatPasswords[i].ChatRoomPassword;

			else
			{
				StoredChatPasswords[i].ChatRoomPassword = ChanPwd;
				SaveConfig();
			}

			break;
		}
	}

	if ( i == StoredChatPasswords.Length && ChanPwd != "" )
	{
		StoredChatPasswords.Length = i + 1;
		StoredChatPasswords[i].ChatRoomName = ChanName;
		StoredChatPasswords[i].ChatRoomPassword = ChanPwd;
		SaveConfig();
	}

	log("Join "$ChanName@"Password:"$ChanPwd@"PRI:"$PlayerReplicationInfo.PlayerName@"Team:"$PlayerReplicationInfo.Team,'VoiceChat');
	if (PlayerReplicationInfo != None && PlayerReplicationInfo.Team != None)
		idx = PlayerReplicationInfo.Team.TeamIndex;

	VCR = VoiceReplicationInfo.GetChannel(ChanName, idx);
	if (VCR != None)
	{
		if (!VCR.IsMember(PlayerReplicationInfo))
			ServerJoinVoiceChannel(VCR.ChannelIndex, ChanPwd);
	}
	else if ( ChatRoomMessageClass != None )
		ClientMessage(ChatRoomMessageClass.static.AssembleMessage(0,ChanName));
}

// Leave a voice chatroom by name
exec function Leave(string ChannelTitle)
{
	local VoiceChatRoom VCR;
	local int idx;

	if (VoiceReplicationInfo == None || !VoiceReplicationInfo.bEnableVoiceChat )
		return;

	if (PlayerReplicationInfo != None && PlayerReplicationInfo.Team != None)
		idx = PlayerReplicationInfo.Team.TeamIndex;

	VCR = VoiceReplicationInfo.GetChannel(ChannelTitle, idx);
	if (VCR == None && ChatRoomMessageClass != None)
	{
		ClientMessage(ChatRoomMessageClass.static.AssembleMessage(0,ChannelTitle));
		return;
	}

	if ( VCR == ActiveRoom )
		ActiveRoom = None;

	ServerLeaveVoiceChannel(VCR.ChannelIndex);
}

// Set a voice chatroom to your active channel
exec function Speak(string ChannelTitle)
{
	local int idx;
	local VoiceChatRoom VCR;
	local string ChanPwd;

    //if _RO_
	if (VoiceReplicationInfo == None || !VoiceReplicationInfo.bEnableVoiceChat  || !bVoiceChatEnabled)
		return;
	//else
	//if (VoiceReplicationInfo == None || !VoiceReplicationInfo.bEnableVoiceChat)
	//	return;
	//end _RO_

	if (PlayerReplicationInfo != None && PlayerReplicationInfo.Team != None)
		idx = PlayerReplicationInfo.Team.TeamIndex;

	// Check that we are a member of this room
	VCR = VoiceReplicationInfo.GetChannel(ChannelTitle, idx);
	if (VCR == None && ChatRoomMessageClass != None)
	{
		ClientMessage(ChatRoomMessageClass.static.AssembleMessage(0,ChannelTitle));
		return;
	}

	if (VCR.ChannelIndex >= 0)
	{
		ChanPwd = FindChannelPassword(ChannelTitle);
		ServerSpeak(VCR.ChannelIndex, ChanPwd);
	}

	else if ( ChatRoomMessageClass != None )
		ClientMessage(ChatRoomMessageClass.static.AssembleMessage(0,ChannelTitle));
}

// Set your active channel to the default channel
exec function SpeakDefault()
{
	local string str;

	str = GetDefaultActiveChannel();
	if ( str != "" && (ActiveRoom == None || !(ActiveRoom.GetTitle() ~= str)) )
		Speak(str);
}

// Set your active channel to the last active channel
exec function SpeakLast()
{
	if ( LastActiveChannel != "" && (ActiveRoom == None || !(ActiveRoom.GetTitle() ~= LastActiveChannel)) )
		Speak(LastActiveChannel);
}

// Change the password for you personal chatroom
exec function SetChatPassword(string NewPassword)
{
	if (ChatPassword != NewPassword)
	{
		ChatPassword = NewPassword;
		SaveConfig();

		ServerSetChatPassword(NewPassword);
	}
}

exec function EnableVoiceChat()
{
	local bool bCurrent;

	bCurrent = bool(ConsoleCommand("get ini:Engine.Engine.AudioDevice UseVoIP"));
	ConsoleCommand("set ini:Engine.Engine.AudioDevice UseVoIP"@True);

	if ( VoiceReplicationInfo == None )
		return;

	if ( !VoiceReplicationInfo.bEnableVoiceChat )
	{
		ChatRoomMessage(15, -1);
		return;
	}

	ChangeVoiceChatMode( True );
	InitializeVoiceChat();

	// TODO What else needs to be done before a sound reboot?
	if (bCurrent == False)
		ConsoleCommand("SOUND_REBOOT");
}

exec function DisableVoiceChat()
{
	local bool bCurrent;

	bCurrent = bool(ConsoleCommand("get ini:Engine.Engine.AudioDevice UseVoIP"));
	ConsoleCommand("set ini:Engine.Engine.AudioDevice UseVoIP"@False);

	if (VoiceReplicationInfo == None || !VoiceReplicationInfo.bEnableVoiceChat )
		return;

	ChangeVoiceChatMode( False );

	// TODO What else needs to be done before a sound reboot?
	if (bCurrent == True)
		ConsoleCommand("SOUND_REBOOT");
}

simulated function InitializeVoiceChat()
{
	if ( bVoiceChatEnabled )
	{
		InitPrivateChatRoom();
		AutoJoinVoiceChat();
	}
}

function InitPrivateChatRoom()
{
	ServerChangeVoiceChatMode(True);
	if ( ChatPassword != "" )
		ServerSetChatPassword(ChatPassword);
}

simulated function string GetDefaultActiveChannel()
{
	local string DefaultChannel;

	if ( DefaultActiveChannel != "" )
		DefaultChannel = DefaultActiveChannel;
	else if ( VoiceReplicationInfo != None )
		DefaultChannel = VoiceReplicationInfo.GetDefaultChannel();

	return DefaultChannel;
}

simulated function AutoJoinVoiceChat();
simulated function ChangeVoiceChatMode( bool bEnable )
{
	if (VoiceReplicationInfo == None)
		return;

	bVoiceChatEnabled = bEnable;

	//if _RO_
	if ( !bEnable )
	    ActiveRoom = none;
	//end _RO_

	if (Level.NetMode == NM_Client || Level.NetMode == NM_ListenServer)
		ServerChangeVoiceChatMode( bEnable );
}

simulated function bool ChatBan(int PlayerID, byte Type)
{
	log("ChatBan Role:"$GetEnum(enum'ENetRole', Role)@"ChatManager:"$ChatManager@"PlayerID:"$PlayerID@"Type:"$Type,'ChatManager');
	if ( Level.NetMode == NM_StandAlone || Level.NetMode == NM_DedicatedServer )
		return false;

	if ( ChatManager == None )
		return false;

	if ( ChatManager.SetRestrictionID(PlayerID, Type) )
	{
		ServerChatRestriction(PlayerID, Type);
		return true;
	}

	log(Name@"ChatBan not successful - could not find player with ID:"@PlayerID,'ChatManager');
	return false;
}

simulated function SetChannelPassword(string ChannelName, string ChannelPassword)
{
	local int i;

	if ( Level.NetMode == NM_DedicatedServer )
		return;

	for ( i = 0; i < StoredChatPasswords.Length; i++ )
	{
		if ( StoredChatPasswords[i].ChatRoomName ~= ChannelName )
			break;
	}

	if ( i == StoredChatPasswords.Length )
		StoredChatPasswords.Length = i + 1;

	StoredChatPasswords[i].ChatRoomName = ChannelName;
	StoredChatPasswords[i].ChatRoomPassword = ChannelPassword;
	SaveConfig();
}

simulated function string FindChannelPassword(string ChannelName)
{
	local int i;

	for ( i = 0; i < StoredChatPasswords.Length; i++ )
		if ( StoredChatPasswords[i].ChatRoomName ~= ChannelName )
			return StoredChatPasswords[i].ChatRoomPassword;

	return "";
}

function VoiceChatRoom.EJoinChatResult ServerJoinVoiceChannel(int ChannelIndex, optional string ChannelPassword)
{
	local VoiceChatRoom VCR;
	local VoiceChatRoom.EJoinChatResult Result;

	VCR = VoiceReplicationInfo.GetChannelAt(ChannelIndex);
	if (VoiceReplicationInfo == None || PlayerReplicationInfo == None || VCR == None || !VoiceReplicationInfo.bEnableVoiceChat)
		return JCR_Invalid;

	if ( VoiceReplicationInfo != None )
		Result = VoiceReplicationInfo.JoinChannelAt(ChannelIndex, PlayerReplicationInfo, ChannelPassword);

	// Take the appropriate action depending on the result received from the VoiceReplicationInfo
	switch ( Result )
	{
		case JCR_NeedPassword:  ClientOpenMenu(ChatPasswordMenuClass, false, VCR.GetTitle(), "NEEDPW");     break;
		case JCR_WrongPassword: ClientOpenMenu(ChatPasswordMenuClass, False, VCR.GetTitle(), "WRONGPW");	break;
		case JCR_Success:       Level.Game.ChangeVoiceChannel(PlayerReplicationInfo, ChannelIndex, -1);
		default:
			if ( ChannelIndex>VoiceReplicationInfo.GetPublicChannelCount(true) )
				ChatRoomMessage(Result, ChannelIndex);

	}

	return Result;
}

function ServerLeaveVoiceChannel(int ChannelIndex)
{
	local VoiceChatRoom VCR;

	if (VoiceReplicationInfo == None || PlayerReplicationInfo == None)
		return;

	if ( !VoiceReplicationInfo.bEnableVoiceChat )
	{
		ChatRoomMessage(15, -1);
		return;
	}

	VCR = VoiceReplicationInfo.GetChannelAt(ChannelIndex);
	if (VCR != None && VCR.LeaveChannel(PlayerReplicationInfo))
	{
		if (VCR == ActiveRoom)
		{
			ActiveRoom = None;
			if ( PlayerReplicationInfo != None )
				PlayerReplicationInfo.ActiveChannel = -1;

// not necessary as client will do this itself
//			ClientSetActiveRoom(-1);
		}

		Level.Game.ChangeVoiceChannel( PlayerReplicationInfo, -1, ChannelIndex );
		if ( ChannelIndex>VoiceReplicationInfo.GetPublicChannelCount(true) )
			ChatRoomMessage(8, ChannelIndex);
	}
}

function ServerSpeak(int ChannelIndex, optional string ChannelPassword)
{
	local VoiceChatRoom VCR;
	local int Index;

	if (VoiceReplicationInfo == None)
		return;

	VCR = VoiceReplicationInfo.GetChannelAt(ChannelIndex);
	if ( VCR == None )
	{
		if ( VoiceReplicationInfo.bEnableVoiceChat )
			ChatRoomMessage(0, ChannelIndex);

		else ChatRoomMessage(15, ChannelIndex);
		return;
	}

	if ( !VCR.IsMember(PlayerReplicationInfo) )
	{
		if ( ServerJoinVoiceChannel(ChannelIndex, ChannelPassword) != JCR_Success )
			return;
	}

	Index = -1;
	if (ActiveRoom == VCR)
	{
		ChatRoomMessage(10, ChannelIndex);
		log(PlayerReplicationInfo.PlayerName@"no longer speaking on "$VCR.GetTitle(),'VoiceChat');
		ActiveRoom = None;
		ClientSetActiveRoom(-1);
	}
	else
	{
		ActiveRoom = VCR;
		log(PlayerReplicationInfo.PlayerName@"speaking on"@VCR.GetTitle(),'VoiceChat');
		ChatRoomMessage(9, ChannelIndex);
		ClientSetActiveRoom(VCR.ChannelIndex);
		Index = VCR.ChannelIndex;
	}

	if ( PlayerReplicationInfo != None )
		PlayerReplicationinfo.ActiveChannel = Index;
}

function ServerSetChatPassword(string NewPassword)
{
	ChatPassword = NewPassword;

	if (PlayerReplicationInfo != None)
		PlayerReplicationInfo.SetChatPassword(NewPassword);
}

function ServerChangeVoiceChatMode( bool bEnable )
{
	if (VoiceReplicationInfo == None)
		return;

	bVoiceChatEnabled = bEnable;

	//if _RO_
	if ( !bEnable )
	    ActiveRoom = none;
	//end _RO_

	if ( bVoiceChatEnabled )
	{
		if ( VoiceReplicationInfo.bEnableVoiceChat )
			VoiceReplicationInfo.AddVoiceChatter(PlayerReplicationInfo);
		else ChatRoomMessage(15, -1);
	}
	else VoiceReplicationInfo.RemoveVoiceChatter(PlayerReplicationInfo);
}

simulated function ClientSetActiveRoom(int ChannelIndex)
{
	if ( VoiceReplicationInfo == None || !bVoiceChatEnabled )
		return;

	if ( ActiveRoom != None )
		LastActiveChannel = ActiveRoom.GetTitle();
	else LastActiveChannel = "";

	ActiveRoom = VoiceReplicationInfo.GetChannelAt(ChannelIndex);
}

// =====================================================================================================================
// =====================================================================================================================
//  Chat Manager
// =====================================================================================================================
// =====================================================================================================================

exec function ChatDebug()
{
	ChatManager.ChatDebug();

	ServerChatDebug();
}

function ServerChatDebug();
function ServerRequestBanInfo(int PlayerID);
function ServerChatRestriction(int PlayerID, byte Type)
{
	local PlayerReplicationInfo PRI;
	local int i;

	log("ServerChatRestriction PlayerID:"$PlayerID@"Type:"$Type,'ChatManager');
	if ( ChatManager == None || GameReplicationInfo == None)
		return;

	for ( i = 0; i < GameReplicationInfo.PRIArray.Length; i++ )
	{
		log("ServerChatRestriction checking GRI.PRIArray["$i$"].PlayerID:"$GameReplicationInfo.PRIArray[i].PlayerID,'ChatManager');
		if ( GameReplicationInfo.PRIArray[i] != None && GameReplicationInfo.PRIArray[i].PlayerID == PlayerID )
		{
			PRI = GameReplicationInfo.PRIArray[i];
			break;
		}
	}

	log("ServerChatRestriction PRI:"$PRI@"PRI.Owner"$PRI.Owner,'ChatManager');

//if _RO_
	if ( PRI == None )
		return;

	ChatManager.SetRestrictionID(PRI.PlayerID, Type);
/*else
	if ( PRI == None || PlayerController(PRI.Owner) == None )
		return;

	ChatManager.SetRestriction(PlayerController(GameReplicationInfo.PRIArray[i].Owner).GetPlayerIDHash(), Type); */
//end _RO_


	// If we added a ban for this player and this player is currently in our private chat room, remove the player
	if ( bool(Type & 8) && PlayerReplicationInfo != None && PlayerReplicationInfo.PrivateChatRoom != None &&
	     PlayerReplicationInfo.PrivateChatRoom.IsMember(PRI, True) )
	{
		ChatRoomMessage(13, -1, PRI);
		PlayerReplicationInfo.PrivateChatRoom.RemoveMember(PRI);
	}
}

simulated event GainedChild( Actor Other )
{
	Super.GainedChild(Other);

	if ( VotingReplicationInfoBase(Other) != None )
		VoteReplicationInfo = VotingReplicationInfoBase(Other);
}

simulated event LostChild(Actor Other)
{
	Super.LostChild(Other);

	if ( VotingReplicationInfoBase(Other) != None )
		VoteReplicationInfo = None;
}

exec function ShowVoteMenu()
{
	if( Level.NetMode != NM_StandAlone &&  VoteReplicationInfo != none && VoteReplicationInfo.MapVoteEnabled() )
		Player.GUIController.OpenMenu(Player.GUIController.GetPropertyText("MapVotingMenu"));
}

// if _RO_

// Semi hax: Used to get mouse sensitivity values for config menu (for some
// reason getting them from PlayerInput defaults doesn't work ingame)
function float GetMouseSensitivity()
{
    if (PlayerInput == none)
        return class'PlayerInput'.default.MouseSensitivity;
    else
        return PlayerInput.MouseSensitivity;
}

function float GetMouseAcceleration()
{
    if (PlayerInput == none)
        return class'PlayerInput'.default.MouseAccelThreshold;
    else
        return PlayerInput.MouseAccelThreshold;
}

function float GetMouseSmoothingStrength()
{
    if (PlayerInput == none)
        return class'PlayerInput'.default.MouseSmoothingStrength;
    else
        return PlayerInput.MouseSmoothingStrength;
}

function ServerInitializeSteamStatInt(byte Index, int Value)
{
	if ( SteamStatsAndAchievements != none )
	{
		SteamStatsAndAchievements.InitializeSteamStatInt(Index, Value);
	}
}

function ServerInitializeSteamStatFloat(byte Index, float Value)
{
	if ( SteamStatsAndAchievements != none )
	{
		SteamStatsAndAchievements.InitializeSteamStatFloat(Index, Value);
	}
}

function ServerSteamStatsAndAchievementsInitialized()
{
	if ( SteamStatsAndAchievements != none )
	{
		SteamStatsAndAchievements.ServerSteamStatsAndAchievementsInitialized();
	}
}

// Steam Workshop
simulated function SyncSteamWorkshop()
{
	EnumerateSubscribedSteamWorkshopFiles();
}

simulated event OnSubscribedFilesEnumerated(int FileCount)
{
    TotalSubscribedFiles = FileCount;
    NextSubscribedFileToFetch = 1;

	//log("KFPlayerController.OnSubscribedFilesEnumerated" @ FileCount,'Workshop_Debug');
    if ( FileCount > 0 )
	{
        GetSubscribedSteamWorkshopFileDetails(0);
	}
}

simulated event OnGetSubscribedFileDetailsCompleted(int SubscribedFileIndex, string Title, bool bFileExistsLocally)
{
	//log("KFPlayerController.OnGetSubscribedFileDetailsCompleted" @ SubscribedFileIndex @ Title @ bFileExistsLocally,'Workshop_Debug');
    if ( !bFileExistsLocally )
	{
		SubscribedFileDownloadIndex = SubscribedFileIndex;
		SubscribedFileDownloadTitle = Title;
        DownloadSubscribedSteamWorkshopFile(SubscribedFileIndex);
	}
    else if ( NextSubscribedFileToFetch < TotalSubscribedFiles )
	{
        GetSubscribedSteamWorkshopFileDetails(NextSubscribedFileToFetch++);
	}
}

simulated event OnSubscribedFileDownloadUpdate(float DownloadProgress)
{
	// TODO: Update UI Output using SubscribedFileDownloadIndex

	//log("KFPlayerController.OnSubscribedFileDownloadUpdate" @ SubscribedFileDownloadIndex @ SubscribedFileDownloadTitle @ DownloadProgress,'Workshop_Debug');
	DownloadFileProgress = DownloadProgress;
    if ( DownloadProgress >= 1.0 )
	{
		SubscribedFileDownloadIndex = -1;

		if ( NextSubscribedFileToFetch < TotalSubscribedFiles )
		{
			GetSubscribedSteamWorkshopFileDetails(NextSubscribedFileToFetch++);
		}
	}
}
// end if _RO_

defaultproperties
{
    TeamBeaconMaxDist=6000.f
    TeamBeaconPlayerInfoMaxDist=1800.f
    TeamBeaconTeamColors[0]=(R=180,G=0,B=0,A=255)
    TeamBeaconTeamColors[1]=(R=80,G=80,B=200,A=255)
    TeamBeaconCustomColor=(R=255,G=255,B=0,A=255)
    bAimingHelp=false
    OrthoZoom=+40000.000000
    FlashScale=(X=1.000000,Y=1.000000,Z=1.000000)
    AnnouncerLevel=2
    FOVAngle=90.000
    DesiredFOV=90.000000
    DefaultFOV=90.000000
    Handedness=1.000000
    bAlwaysMouseLook=True
    ViewingFrom="Now viewing from"
    OwnCamera="Now viewing from own camera"
    QuickSaveString="Quick Saving"
    NoPauseMessage="Game is not pauseable"
    bTravel=True
    bStasis=False
    NetPriority=3
    LocalMessageClass=class'LocalMessage'
    bIsPlayer=true
    bCanOpenDoors=true
    bCanDoSpecial=true
    Physics=PHYS_None
    EnemyTurnSpeed=45000
    CheatClass=class'Engine.CheatManager'
    InputClass=class'Engine.PlayerInput'
    CameraDist=+9.0
    CameraDistRange=(Min=3.0,Max=40.0)
    bZeroRoll=true
// ifdef _KF_
    bDynamicNetSpeed=false
// else
//    bDynamicNetSpeed=true
// endif _KF_

    VehicleCheckRadius=700.0

    // jdf ---
    bEnableWeaponForceFeedback=True
    bEnablePickupForceFeedback=True
    bEnableDamageForceFeedback=True
    bEnableGUIForceFeedback=True
    bForceFeedbackSupported=True
    // --- jdf
    ProgressTimeOut=8.0
    bAutoTaunt=false
    AnnouncerVolume=4
    bHideSpectatorBeacons=false
    bWeaponViewShake=true
    ScreenFlashScaling=+1.0
    MaxResponseTime=0.125
    SpectateSpeed=+600.0
    DynamicPingThreshold=+400.0
    ClientCap=0
    bSmallWeapons=true
    bLandingShake=true
    bOnlySpeakTeamText=false
    bNoTextToSpeechVoiceMessages=true
	TextToSpeechVoiceVolume=1.0
	FOVBias=+1.0
	LastSpeedHackLog=-100.0

    // rjp --
    AutoJoinMask=5
	bVoiceChatEnabled=True
	bEnableInitialChatRoom=True
// if _RO_
    MidGameMenuClass="ROInterface.RODisconnectOptionPage"
// else
//    MidGameMenuClass="GUI2K4.UT2K4DisconnectOptionPage"
// end if _RO_
    AdminMenuClass="GUI2K4.RemoteAdmin"
    ChatPasswordMenuClass="GUI2K4.UT2K4ChatPassword"
    ChatRoomMessageClass=class'Engine.ChatRoomMessage'
    PlayerChatType="Engine.PlayerChatManager"
    VoiceChatCodec="CODEC_48NB"
    VoiceChatLANCodec="CODEC_96WB"
    // -- rjp
}
