1
0
KF2-Dev-Scripts/Engine/Classes/GameViewportClient.uc
2020-12-13 18:01:13 +03:00

1670 lines
52 KiB
Ucode

/**
* A game viewport (FViewport) is a high-level abstract interface for the
* platform specific rendering, audio, and input subsystems.
* GameViewportClient is the engine's interface to a game viewport.
* Exactly one GameViewportClient is created for each instance of the game. The
* only case (so far) where you might have a single instance of Engine, but
* multiple instances of the game (and thus multiple GameViewportClients) is when
* you have more than one PIE window running.
*
* Responsibilities:
* propagating input events to the global interactions list
*
*
*
* Copyright 1998-2013 Epic Games, Inc. All Rights Reserved.
*/
class GameViewportClient extends ScriptViewportClient
within Engine
transient
native
Inherits(FExec)
config(Engine)
;
/** The platform-specific viewport which this viewport client is attached to. */
var const pointer Viewport{FViewport};
/** The platform-specific viewport frame which this viewport is contained by. */
var const pointer ViewportFrame{FViewportFrame};
/** A list of interactions which have a chance at all input before the player's interactions. */
var init protected array<Interaction> GlobalInteractions;
/** The class for the UI controller */
var class<UIInteraction> UIControllerClass;
/** The viewport's UI controller */
var UIInteraction UIController;
/** The viewport's console. Might be null on consoles */
var Console ViewportConsole;
/** This struct needs to be the same size as EShowFlags */
struct native ShowFlags_Mirror
{
var native const qword flags0;
var native const qword flags1;
};
/** This empty struct is here so we have a non-native struct that can be exported to the header properly */
struct {EShowFlags} ExportShowFlags_Mirror extends ShowFlags_Mirror {};
/** The show flags used by the viewport's players. */
var const ExportShowFlags_Mirror ShowFlags;
/** @name Localized transition messages. */
//@{
var localized string LoadingMessage;
var localized string SavingMessage;
var localized string ConnectingMessage;
var localized string PausedMessage;
var localized string PrecachingMessage;
//@}
/** if TRUE then the title safe border is drawn */
var bool bShowTitleSafeZone;
/** Max/Recommended screen viewable extents as a percentage */
struct native TitleSafeZoneArea
{
var float MaxPercentX;
var float MaxPercentY;
var float RecommendedPercentX;
var float RecommendedPercentY;
};
/** border of safe area */
var TitleSafeZoneArea TitleSafeZone;
//If true, this will cause the hardware mouse cursor to render
var transient bool bDisplayHardwareMouseCursor;
var transient bool bOverrideDiffuseAndSpecular;
/** If TRUE, this viewport is a play in editor viewport */
var transient bool bIsPlayInEditorViewport;
/** If TRUE, we will show the OS mouse cursor at all times (only applies to PIE viewports)*/
var transient bool bShowSystemMouseCursor;
/**
* Enum of the different splitscreen types
*/
enum ESplitScreenType
{
eSST_NONE, // No split
eSST_2P_HORIZONTAL, // 2 player horizontal split
eSST_2P_VERTICAL, // 2 player vertical split
eSST_3P_FAVOR_TOP, // 3 Player split with 1 player on top and 2 on bottom
eSST_3P_FAVOR_BOTTOM, // 3 Player split with 1 player on bottom and 2 on top
eSST_4P, // 4 Player split
};
/**
* The 4 different kinds of safezones
*/
enum ESafeZoneType
{
eSZ_TOP,
eSZ_BOTTOM,
eSZ_LEFT,
eSZ_RIGHT,
};
/**
* Specifies whether a setting should be toggled, enabled or disabled.
*/
enum ESetMode
{
SetMode_Toggle,
SetMode_Enable,
SetMode_Disable,
};
/**
* Structure to store splitscreen data.
*/
struct native PerPlayerSplitscreenData
{
var float SizeX;
var float SizeY;
var float OriginX;
var float OriginY;
};
/**
* Structure containing all the player splitscreen datas per splitscreen configuration.
*/
struct native SplitscreenData
{
var array<PerPlayerSplitscreenData> PlayerData;
};
/** Array of the screen data needed for all the different splitscreen configurations */
var array<SplitscreenData> SplitscreenInfo;
/**
* The splitscreen layout type that the player wishes to use; this value usually comes from places like the player's profile
*/
var protected{protected} ESplitScreenType DesiredSplitscreenType;
/**
* The splitscreen type that is actually being used; takes into account the number of players and other factors (such as cinematic mode)
* that could affect the splitscreen mode that is actually used.
*/
var protected{protected} ESplitscreenType ActiveSplitscreenType;
/** Defaults for intances where there are multiple configs for a certain number of players */
var const ESplitScreenType Default2PSplitType;
var const ESplitScreenType Default3PSplitType;
/** Set to disable world rendering */
var bool bDisableWorldRendering;
/** Set to capture the world rendering into an offscreen target */
var bool bCapturedWorldRendering;
// Progress Indicator - used by the engine to provide status messages (see SetProgressMessage())
var string ProgressMessage[2];
var float ProgressTimeOut;
var float ProgressFadeTime;
/** debug property display functionality
* to interact with this, use "display", "displayall", "displayclear"
*/
struct native DebugDisplayProperty
{
/** the object whose property to display. If this is a class, all objects of that class are drawn. */
var Object Obj;
/** name of the property to display */
var name PropertyName;
/** whether PropertyName is a "special" value not directly mapping to a real property (e.g. state name) */
var bool bSpecialProperty;
};
var array<DebugDisplayProperty> DebugProperties;
/** Stores the pointer to any data needed for scaleform (if defined)*/
var native const pointer ScaleformInteraction { UGFxInteraction };
/** DEBUG: If TRUE, the GFx UI will NOT be rendered at runtime. Note that to REMOVE GFx functionality permanently, you should compile with WITH_GFx set to 0. This bool is for debugging only. */
var config bool bDebugNoGFxUI;
//@HSL_BEGIN - BWJ - Dingo controller support
/** TRUE if input is allowed from multiple controllers */
var bool bAllowInputFromMultipleControllers;
/** TRUE if we need to pair a new gamepad due a controller disconnect */
var bool bNeedsNewGamepadPairingForControllerDisconnect;
/** TRUE if we need to pair a new gamepad because of a new profile taking over. We aren't sure which gamepad to use */
var bool bNeedsNewGamepadPairingForNewProfile;
//@HSL_END
cpptext
{
/** Make sure that the UC mirror struct matches the size of EShowFlags */
checkAtCompileTime( sizeof(FShowFlags_Mirror) == sizeof(EShowFlags), ShowFlags_Mirror__MustMatchSizeOfEShowFlags );
// Constructor.
UGameViewportClient();
/**
* Cleans up all rooted or referenced objects created or managed by the GameViewportClient. This method is called
* when this GameViewportClient has been disassociated with the game engine (i.e. is no longer the engine's GameViewport).
*/
virtual void DetachViewportClient();
/**
* Called every frame to allow the game viewport to update time based state.
* @param DeltaTime The time since the last call to Tick.
*/
void Tick( FLOAT DeltaTime );
// FViewportClient interface.
virtual void RedrawRequested(FViewport* InViewport) {}
/**
* Routes an input key event received from the viewport to the Interactions array for processing.
*
* @param Viewport the viewport the input event was received from
* @param ControllerId gamepad/controller that generated this input event
* @param Key the name of the key which an event occured for (KEY_Up, KEY_Down, etc.)
* @param EventType the type of event which occured (pressed, released, etc.)
* @param AmountDepressed (analog keys only) the depression percent.
* @param bGamepad - input came from gamepad (ie xbox controller)
*
* @return TRUE to consume the key event, FALSE to pass it on.
*/
virtual UBOOL InputKey(FViewport* Viewport,INT ControllerId,FName Key,EInputEvent EventType,FLOAT AmountDepressed=1.f,UBOOL bGamepad=FALSE);
/**
* Routes an input axis (joystick, thumbstick, or mouse) event received from the viewport to the Interactions array for processing.
*
* @param Viewport the viewport the input event was received from
* @param ControllerId the controller that generated this input axis event
* @param Key the name of the axis that moved (KEY_MouseX, KEY_XboxTypeS_LeftX, etc.)
* @param Delta the movement delta for the axis
* @param DeltaTime the time (in seconds) since the last axis update.
*
* @return TRUE to consume the axis event, FALSE to pass it on.
*/
virtual UBOOL InputAxis(FViewport* Viewport,INT ControllerId,FName Key,FLOAT Delta,FLOAT DeltaTime, UBOOL bGamepad=FALSE);
/**
* Routes a character input event (typing) received from the viewport to the Interactions array for processing.
*
* @param Viewport the viewport the input event was received from
* @param ControllerId the controller that generated this character input event
* @param Character the character that was typed
*
* @return TRUE to consume the key event, FALSE to pass it on.
*/
virtual UBOOL InputChar(FViewport* Viewport,INT ControllerId,TCHAR Character);
/**
* Check a key event received by the viewport.
* If the viewport client uses the event, it should return true to consume it.
* @param Viewport - The viewport which the event is from.
* @param ControllerId - The controller which the key event is from.
* @param Handle - Identifier unique to this touch event
* @param Type - What kind of touch event this is (see ETouchType)
* @param TouchLocation - Screen position of the touch
* @param DeviceTimestamp - Timestamp of the event
* @param TouchpadIndex - For devices with multiple touchpads, this is the index of which one
* @return True to consume the key event, false to pass it on.
*/
virtual UBOOL InputTouch(FViewport* Viewport, INT ControllerId, UINT Handle, BYTE Type, FVector2D TouchLocation, DOUBLE DeviceTimestamp, UINT TouchpadIndex=0);
/**
* Each frame, the input system will update the motion data.
*
* @param Viewport - The viewport which the key event is from.
* @param ControllerId - The controller which the key event is from.
* @param Tilt The current orientation of the device
* @param RotationRate How fast the tilt is changing
* @param Gravity Describes the current gravity of the device
* @param Acceleration Describes the acceleration of the device
* @return True to consume the motion event, false to pass it on.
*/
virtual UBOOL InputMotion(FViewport* Viewport, INT ControllerId, const FVector& Tilt, const FVector& RotationRate, const FVector& Gravity, const FVector& Acceleration);
/** Returns the platform specific forcefeedback manager associated with this viewport */
virtual class UForceFeedbackManager* GetForceFeedbackManager(INT ControllerId);
/**
* @return the splitscreen type that is currently being used
*/
FORCEINLINE ESplitScreenType GetCurrentSplitscreenType() const
{
return static_cast<ESplitScreenType>(ActiveSplitscreenType);
}
/**
* Retrieves the cursor that should be displayed by the OS
*
* @param Viewport the viewport that contains the cursor
* @param X the x position of the cursor
* @param Y the Y position of the cursor
*
* @return the cursor that the OS should display
*/
virtual EMouseCursor GetCursor( FViewport* Viewport, INT X, INT Y );
virtual void Precache();
virtual void Draw(FViewport* Viewport,FCanvas* Canvas);
virtual void LostFocus(FViewport* Viewport);
virtual void ReceivedFocus(FViewport* Viewport);
virtual UBOOL IsFocused(FViewport* Viewport);
virtual void CloseRequested(FViewport* Viewport);
virtual UBOOL RequiresHitProxyStorage() { return 0; }
/**
* Determines whether this viewport client should receive calls to InputAxis() if the game's window is not currently capturing the mouse.
* Used by the UI system to easily receive calls to InputAxis while the viewport's mouse capture is disabled.
*/
virtual UBOOL RequiresUncapturedAxisInput() const;
// FExec interface.
virtual UBOOL Exec(const TCHAR* Cmd,FOutputDevice& Ar);
/**
* Helper function to toggles, enable or disable the specified show flag. Called by Exec().
*
* @param Cmd Exec command line, as passed on from Exec().
* @param Ar Output device used for reporting the result.
* @param SetMode Specifies whether the flag should be toggled, enabled or disabled.
* @return TRUE if the flag was modified.
*/
UBOOL SetShowFlags(const TCHAR* Cmd,FOutputDevice& Ar, ESetMode SetMode );
/**
* Set this GameViewportClient's viewport and viewport frame to the viewport specified
*/
virtual void SetViewportFrame( FViewportFrame* InViewportFrame );
/**
* Set this GameViewportClient's viewport to the viewport specified
*/
virtual void SetViewport( FViewport* InViewportFrame );
/** sets bDropDetail and other per-frame detail level flags on the current WorldInfo
* @param DeltaSeconds - amount of time passed since last tick
*/
virtual void SetDropDetail(FLOAT DeltaSeconds);
#if __TW_
/** Stub for KF2 level ShowSpawnVolumes (see KFGameViewportClient) */
virtual void ShowSpawnVolumes( ESetMode SetMode ) { }
#endif
#if WITH_GFx
virtual UObject* GetUObject() { return this; }
#endif
}
/**
* Provides script-only child classes the opportunity to handle input key events received from the viewport.
* This delegate is called before the input key event is passed to the interactions array for processing.
*
* @param ControllerId the controller that generated this input key event
* @param Key the name of the key which an event occured for (KEY_Up, KEY_Down, etc.)
* @param EventType the type of event which occured (pressed, released, etc.)
* @param AmountDepressed for analog keys, the depression percent.
* @param bGamepad input came from gamepad (ie xbox controller)
*
* @return return TRUE to indicate that the input event was handled. if the return value is TRUE, this input event will not
* be passed to the interactions array.
*/
delegate bool HandleInputKey( int ControllerId, name Key, EInputEvent EventType, float AmountDepressed, optional bool bGamepad );
/**
* Provides script-only child classes the opportunity to handle input axis events received from the viewport.
* This delegate is called before the input axis event is passed to the interactions array for processing.
*
* @param ControllerId the controller that generated this input axis event
* @param Key the name of the axis that moved (KEY_MouseX, KEY_XboxTypeS_LeftX, etc.)
* @param Delta the movement delta for the axis
* @param DeltaTime the time (in seconds) since the last axis update.
* @param bGamepad input came from gamepad (ie xbox controller)
*
* @return return TRUE to indicate that the input event was handled. if the return value is TRUE, this input event will not
* be passed to the interactions array.
*/
delegate bool HandleInputAxis( int ControllerId, name Key, float Delta, float DeltaTime, bool bGamepad);
/**
* Provides script-only child classes the opportunity to handle character input (typing) events received from the viewport.
* This delegate is called before the character event is passed to the interactions array for processing.
*
* @param ControllerId the controller that generated this character input event
* @param Unicode the character that was typed
*
* @return return TRUE to indicate that the input event was handled. if the return value is TRUE, this input event will not
* be passed to the interactions array.
*/
delegate bool HandleInputChar( int ControllerId, string Unicode );
/**
* Executes a console command in the context of this viewport.
* @param Command - The command to execute.
* @return The output of the command will be returned.
*/
native function string ConsoleCommand(string Command);
/**
* Retrieve the size of the main viewport.
*
* @param out_ViewportSize [out] will be filled in with the size of the main viewport
*/
native final function GetViewportSize( out Vector2D out_ViewportSize );
/** @return Whether or not the main viewport is fullscreen or windowed. */
native final function bool IsFullScreenViewport();
/** @return mouse position in game viewport coordinates (does not account for splitscreen) */
native final function vector2D GetMousePosition();
/**
* Determine whether a fullscreen viewport should be used in cases where there are multiple players.
*
* @return TRUE to use a fullscreen viewport; FALSE to allow each player to have their own area of the viewport.
*/
native final function bool ShouldForceFullscreenViewport() const;
/**Function that allow for custom numbers of interactions dictated in code*/
native function int GetNumCustomInteractions();
/**Defining the above mentioned custom interactions*/
native function class<UIInteraction> GetCustomInteractionClass(int InIndex);
/**Passing the custom interaction object back to native code to do with it as it likes*/
native function SetCustomInteractionObject(Interaction InInteraction);
/** Function to notify GFx of a change in the splitscreen layout*/
native function NotifySplitscreenLayoutChanged();
/** Force the mouse cursor to update */
native function ForceUpdateMouseCursor(bool bSetCursor);
/** Move the hardware mouse cursor to the designated location*/
native function SetMouse(int X, int Y);
/**
* Adds a new player.
* @param ControllerId - The controller ID the player should accept input from.
* @param OutError - If no player is returned, OutError will contain a string describing the reason.
* @param SpawnActor - True if an actor should be spawned for the new player.
* @return The player which was created.
*/
event LocalPlayer CreatePlayer(int ControllerId, out string OutError, bool bSpawnActor)
{
local LocalPlayer NewPlayer;
local int InsertIndex;
`log("Creating new player with ControllerId" @ ControllerId @ "(" $ GamePlayers.Length @ "existing players)",,'PlayerManagement');
Assert(LocalPlayerClass != None);
NewPlayer = new(Outer) LocalPlayerClass;
NewPlayer.ViewportClient = Self;
NewPlayer.ControllerId = ControllerId;
InsertIndex = AddLocalPlayer(NewPlayer);
if ( bSpawnActor && InsertIndex != INDEX_NONE )
{
if (GetCurrentWorldInfo().NetMode != NM_Client)
{
// server; spawn a new PlayerController immediately
if (!NewPlayer.SpawnPlayActor("", OutError))
{
RemoveLocalPlayer(NewPlayer);
NewPlayer = None;
}
}
else
{
// client; ask the server to let the new player join
NewPlayer.SendSplitJoin();
}
}
if (OutError != "")
{
`Log("Player creation failed with error:" @ OutError);
}
else
{
`log("Successfully created new player with ControllerId" @ ControllerId $ ":" @ NewPlayer @ "- inserted into GamePlayers array at index" @ InsertIndex
@ "(" $ GamePlayers.Length @ "existing players)",,'PlayerManagement');
if ( NewPlayer != None && InsertIndex != INDEX_NONE )
{
// let all interactions know about this
NotifyPlayerAdded(InsertIndex, NewPlayer);
}
}
return NewPlayer;
}
/**
* Removes a player.
* @param Player - The player to remove.
* @return whether the player was successfully removed. Removal is not allowed while connected to a server.
*/
event bool RemovePlayer(LocalPlayer ExPlayer)
{
local int OldIndex, i;
//Mapping of: index into array is new index, value is old index
local array<int> IDMappings;
// can't destroy viewports while connected to a server
if (ExPlayer.Actor.Role == ROLE_Authority)
{
`log("Removing player" @ ExPlayer @ " with ControllerId" @ ExPlayer.ControllerId @ "at index" @ GamePlayers.Find(ExPlayer)@ "(" $ GamePlayers.Length @ "existing players)",,'PlayerManagement');
`if(`isdefined(FIXING_SIGNIN_ISSUES))
ScriptTrace();
`endif
// Disassociate this viewport client from the player.
ExPlayer.ViewportClient = None;
if ( ExPlayer.Actor != None )
{
// Destroy the player's actors.
ExPlayer.Actor.Destroy();
}
// Remove the player from the global and viewport lists of players.
OldIndex = RemoveLocalPlayer(ExPlayer);
if ( OldIndex != INDEX_NONE )
{
// let all interactions know about this
// NOTE: This is where GFx will clean up any movies that used to reference that index
NotifyPlayerRemoved(OldIndex, ExPlayer);
}
//Now we need to handle the fact that we may have screwed up the player indices for other gfx movies
//If they were equal we would have removed the last element, so we wouldn't need to do anything at all
if (OldIndex != GamePlayers.Length)
{
//Otherwise we have to cleanup
for (i = 0; i < GamePlayers.Length; i++)
{
//If it is below the removal, everything is the same
if (i < OldIndex)
{
IDMappings.AddItem(i);
}
else //If it is at the removal point or greater everything is one less than what it used to be
{
IDMappings.AddItem(i+1);
}
}
}
//If we constructed any IDMappings, then something must be fixed up, so do so
if (IDMappings.Length > 0)
{
FixupOwnerReferences(IDMappings);
}
`log("Finished removing player " @ ExPlayer @ " with ControllerId" @ ExPlayer.ControllerId @ "at index" @ OldIndex@ "(" $ GamePlayers.Length @ "remaining players)",,'PlayerManagement');
return true;
}
else
{
`log("Not removing player" @ ExPlayer @ " with ControllerId" @ ExPlayer.ControllerId @ "because player does not have appropriate role (" $ GetEnum(enum'ENetRole',ExPlayer.Actor.Role) $ ")",,'PlayerManagement');
return false;
}
}
/**
* Finds a player by controller ID.
* @param ControllerId - The controller ID to search for.
* @return None or the player with matching controller ID.
*/
final event LocalPlayer FindPlayerByControllerId(int ControllerId)
{
local int PlayerIndex;
for(PlayerIndex = 0;PlayerIndex < GamePlayers.Length;PlayerIndex++)
{
if(GamePlayers[PlayerIndex].ControllerId == ControllerId)
{
return GamePlayers[PlayerIndex];
}
}
return None;
}
`if(`notdefined(ShippingPC))
/**
* Debug console command to create a player.
* @param ControllerId - The controller ID the player should accept input from.
*/
exec function DebugCreatePlayer(int ControllerId)
{
local string Error;
CreatePlayer(ControllerId, Error, TRUE);
}
/** Rotates controller ids among gameplayers, useful for testing splitscreen with only one controller. */
exec function SSSwapControllers()
{
local int Idx, TmpControllerID;
TmpControllerID = GamePlayers[0].ControllerID;
for (Idx=0; Idx<GamePlayers.Length-1; ++Idx)
{
GamePlayers[Idx].ControllerID = GamePlayers[Idx+1].ControllerID;
}
GamePlayers[GamePlayers.Length-1].ControllerID = TmpControllerID;
}
/**
* Debug console command to remove the player with a given controller ID.
* @param ControllerId - The controller ID to search for.
*/
exec function DebugRemovePlayer(int ControllerId)
{
local LocalPlayer ExPlayer;
ExPlayer = FindPlayerByControllerId(ControllerId);
if(ExPlayer != None)
{
RemovePlayer(ExPlayer);
}
}
/** debug test for testing splitscreens */
exec function SetSplit( int mode )
{
SetSplitscreenConfiguration( ESplitScreenType(mode) );
}
/**
* Exec for toggling the display of the title safe area
*/
exec function ShowTitleSafeArea()
{
bShowTitleSafeZone = !bShowTitleSafeZone;
}
/**
* Sets the player which console commands will be executed in the context of.
*/
exec function SetConsoleTarget(int PlayerIndex)
{
if (ViewportConsole != none)
{
if(PlayerIndex >= 0 && PlayerIndex < GamePlayers.Length)
{
ViewportConsole.ConsoleTargetPlayer = GamePlayers[PlayerIndex];
}
else
{
ViewportConsole.ConsoleTargetPlayer = None;
}
}
}
`endif
/**
* Initialize the game viewport.
* @param OutError - If an error occurs, returns the error description.
* @return False if an error occurred, true if the viewport was initialized successfully.
*/
event bool Init(out string OutError)
{
local PlayerManagerInteraction PlayerInteraction;
local int NumCustomInteractions;
local class<UIInteraction> CustomInteractionClass;
local UIInteraction CustomInteraction;
local int Idx;
assert(Outer.ConsoleClass != None);
ActiveSplitscreenType = DesiredSplitscreenType;
`if(`notdefined(FINAL_RELEASE))
// Create the viewport's console.
ViewportConsole = new(Self) Outer.ConsoleClass;
if ( InsertInteraction(ViewportConsole) == -1 )
{
OutError = "Failed to add interaction to GlobalInteractions array:" @ ViewportConsole;
return false;
}
`endif
// Initialize custom interactions
NumCustomInteractions = GetNumCustomInteractions();
for ( Idx = 0; Idx < NumCustomInteractions; Idx++ )
{
CustomInteractionClass = GetCustomInteractionClass(Idx);
CustomInteraction = new(Self) CustomInteractionClass;
if ( InsertInteraction(CustomInteraction) == -1 )
{
OutError = "Failed to add interaction to GlobalInteractions array:" @ CustomInteraction;
return false;
}
SetCustomInteractionObject(CustomInteraction);
}
assert(UIControllerClass != None);
// Create a interaction to handle UI input.
UIController = new(Self) UIControllerClass;
if ( InsertInteraction(UIController) == -1 )
{
OutError = "Failed to add interaction to GlobalInteractions array:" @ UIController;
return false;
}
// Create the viewport's player management interaction.
PlayerInteraction = new(Self) class'PlayerManagerInteraction';
if ( InsertInteraction(PlayerInteraction) == -1 )
{
OutError = "Failed to add interaction to GlobalInteractions array:" @ PlayerInteraction;
return false;
}
// Disable the old UI system, if desired for debugging
if( bDebugNoGFxUI )
{
DebugSetUISystemEnabled(TRUE, FALSE);
}
// create the initial player - this is necessary or we can't render anything in-game.
return CreateInitialPlayer(OutError);
}
/**
* Create the game's initial player at startup. First search for a player that is signed into the OnlineSubsystem; if none are found,
* create a player with a ControllerId of 0.
*
* @param OutError receives the error string if an error occurs while creating the player.
*
* @return TRUE if a player was successfully created.
*/
function bool CreateInitialPlayer( out string OutError )
{
local int ControllerId;
local bool bFoundInitialGamepad, bResult;
for ( ControllerId = 0; ControllerId < class'UIRoot'.const.MAX_SUPPORTED_GAMEPADS; ControllerId++ )
{
if ( UIController.IsLoggedIn(ControllerId) )
{
bFoundInitialGamepad = true;
bResult = CreatePlayer(ControllerId, OutError, false) != None;
break;
}
}
if ( !bFoundInitialGamepad || !bResult )
{
// find the first connected gamepad
for ( ControllerId = 0; ControllerId < class'UIRoot'.const.MAX_SUPPORTED_GAMEPADS; ControllerId++ )
{
if ( UIController.IsGamepadConnected(ControllerId) )
{
bFoundInitialGamepad = true;
bResult = CreatePlayer(ControllerId, OutError, false) != None;
break;
}
}
}
if ( !bFoundInitialGamepad || !bResult )
{
bResult = CreatePlayer(0, OutError, false) != None;
}
return bResult;
}
/**
* Inserts an interaction into the GlobalInteractions array at the specified index
*
* @param NewInteraction the interaction that should be inserted into the array
* @param Index the position in the GlobalInteractions array to insert the element.
* if no value (or -1) is specified, inserts the interaction at the end of the array
*
* @return the position in the GlobalInteractions array where the element was placed, or -1 if the element wasn't
* added to the array for some reason
*/
event int InsertInteraction( Interaction NewInteraction, optional int InIndex = -1 )
{
local int Result;
Result = -1;
if ( NewInteraction != None )
{
// if the specified index is -1, assume that the item should be added to the end of the array
if ( InIndex == -1 )
{
InIndex = GlobalInteractions.Length;
}
// if the index is a negative value other than -1, don't add the element as someone made a mistake
if ( InIndex >= 0 )
{
// clamp the Index to avoid expanding the array needlessly
Result = Clamp(InIndex, 0, GlobalInteractions.Length);
// now insert the item
GlobalInteractions.Insert(Result, 1);
GlobalInteractions[Result] = NewInteraction;
NewInteraction.Init();
NewInteraction.OnInitialize();
}
else
{
`warn("Invalid insertion index specified:" @ InIndex);
}
}
return Result;
}
/**
* Called when the current map is being unloaded. Cleans up any references which would prevent garbage collection.
*/
event GameSessionEnded()
{
local int i;
for ( i = 0; i < GlobalInteractions.Length; i++ )
{
GlobalInteractions[i].NotifyGameSessionEnded();
}
}
/**
* Sets the screen layout configuration that the player wishes to use when in split-screen mode.
*/
function SetSplitscreenConfiguration( ESplitScreenType SplitType )
{
DesiredSplitscreenType = SplitType;
}
/**
* @return the actual splitscreen type being used, taking into account the number of players.
*/
function ESplitScreenType GetSplitscreenConfiguration()
{
return ActiveSplitscreenType;
}
/**
* Sets the value of ActiveSplitscreenConfiguration based on the desired split-screen layout type, current number of players, and any other
* factors that might affect the way the screen should be layed out.
*/
function UpdateActiveSplitscreenType()
{
local ESplitScreenType SplitType;
SplitType = DesiredSplitscreenType;
switch ( GamePlayers.Length )
{
case 0:
case 1:
SplitType = eSST_NONE;
break;
case 2:
if ( (SplitType != eSST_2P_HORIZONTAL) && (SplitType != eSST_2P_VERTICAL) )
{
SplitType = Default2PSplitType;
}
break;
case 3:
if ( (SplitType != eSST_3P_FAVOR_TOP) && (SplitType != eSST_3P_FAVOR_BOTTOM) )
{
SplitType = Default3PSplitType;
}
break;
default:
SplitType = eSST_4P;
break;
}
ActiveSplitscreenType = SplitType;
}
/**
* Called before rendering to allow the game viewport to allocate subregions to players.
*/
event LayoutPlayers()
{
local int Idx;
local ESplitScreenType SplitType, PreviousSplitType;
PreviousSplitType = GetSplitscreenConfiguration();
UpdateActiveSplitscreenType();
SplitType = GetSplitscreenConfiguration();
// Initialize the players
for ( Idx = 0; Idx < GamePlayers.Length; Idx++ )
{
if ( SplitType < SplitscreenInfo.Length && Idx < SplitscreenInfo[SplitType].PlayerData.Length )
{
GamePlayers[Idx].Size.X = SplitscreenInfo[SplitType].PlayerData[Idx].SizeX;
GamePlayers[Idx].Size.Y = SplitscreenInfo[SplitType].PlayerData[Idx].SizeY;
GamePlayers[Idx].Origin.X = SplitscreenInfo[SplitType].PlayerData[Idx].OriginX;
GamePlayers[Idx].Origin.Y = SplitscreenInfo[SplitType].PlayerData[Idx].OriginY;
}
else
{
GamePlayers[Idx].Size.X = 0.f;
GamePlayers[Idx].Size.Y = 0.f;
GamePlayers[Idx].Origin.X = 0.f;
GamePlayers[Idx].Origin.Y = 0.f;
}
}
//If splitscreen type has changed, update GFx
if (PreviousSplitType != SplitType)
{
NotifySplitscreenLayoutChanged();
}
}
/** called before rending subtitles to allow the game viewport to determine the size of the subtitle area
* @param Min top left bounds of subtitle region (0 to 1)
* @param Max bottom right bounds of subtitle region (0 to 1)
*/
event GetSubtitleRegion(out vector2D MinPos, out vector2D MaxPos)
{
MaxPos.X = 1.0f;
MaxPos.Y = (GamePlayers.length == 1) ? 0.9f : 0.5f;
}
/**
* Convert a LocalPlayer to it's index in the GamePlayer array
* Returns -1 if the index could not be found.
*/
final function int ConvertLocalPlayerToGamePlayerIndex( LocalPlayer LPlayer )
{
return GamePlayers.Find( LPlayer );
}
/**
* Whether the player at LocalPlayerIndex's viewport has a "top of viewport" safezone or not.
*/
final function bool HasTopSafeZone( int LocalPlayerIndex )
{
switch ( GetSplitscreenConfiguration() )
{
case eSST_NONE:
case eSST_2P_VERTICAL:
return true;
case eSST_2P_HORIZONTAL:
case eSST_3P_FAVOR_TOP:
return (LocalPlayerIndex == 0) ? true : false;
case eSST_3P_FAVOR_BOTTOM:
case eSST_4P:
return (LocalPlayerIndex < 2) ? true : false;
}
return false;
}
/**
* Whether the player at LocalPlayerIndex's viewport has a "bottom of viewport" safezone or not.
*/
final function bool HasBottomSafeZone( int LocalPlayerIndex )
{
switch ( GetSplitscreenConfiguration() )
{
case eSST_NONE:
case eSST_2P_VERTICAL:
return true;
case eSST_2P_HORIZONTAL:
case eSST_3P_FAVOR_TOP:
return (LocalPlayerIndex == 0) ? false : true;
case eSST_3P_FAVOR_BOTTOM:
case eSST_4P:
return (LocalPlayerIndex > 1) ? true : false;
}
return false;
}
/**
* Whether the player at LocalPlayerIndex's viewport has a "left of viewport" safezone or not.
*/
final function bool HasLeftSafeZone( int LocalPlayerIndex )
{
switch ( GetSplitscreenConfiguration() )
{
case eSST_NONE:
case eSST_2P_HORIZONTAL:
return true;
case eSST_2P_VERTICAL:
return (LocalPlayerIndex == 0) ? true : false;
case eSST_3P_FAVOR_TOP:
return (LocalPlayerIndex < 2) ? true : false;
case eSST_3P_FAVOR_BOTTOM:
case eSST_4P:
return (LocalPlayerIndex == 0 || LocalPlayerIndex == 2) ? true : false;
}
return false;
}
/**
* Whether the player at LocalPlayerIndex's viewport has a "right of viewport" safezone or not.
*/
final function bool HasRightSafeZone( int LocalPlayerIndex )
{
switch ( GetSplitscreenConfiguration() )
{
case eSST_NONE:
case eSST_2P_HORIZONTAL:
return true;
case eSST_2P_VERTICAL:
case eSST_3P_FAVOR_BOTTOM:
return (LocalPlayerIndex > 0) ? true : false;
case eSST_3P_FAVOR_TOP:
return (LocalPlayerIndex == 1) ? false : true;
case eSST_4P:
return (LocalPlayerIndex == 0 || LocalPlayerIndex == 2) ? false : true;
}
return false;
}
/**
* Get the total pixel size of the screen.
* This is different from the pixel size of the viewport since we could be in splitscreen
*/
final function GetPixelSizeOfScreen( out float out_Width, out float out_Height, canvas Canvas, int LocalPlayerIndex )
{
switch ( GetSplitscreenConfiguration() )
{
case eSST_NONE:
out_Width = Canvas.ClipX;
out_Height = Canvas.ClipY;
return;
case eSST_2P_HORIZONTAL:
out_Width = Canvas.ClipX;
out_Height = Canvas.ClipY * 2;
return;
case eSST_2P_VERTICAL:
out_Width = Canvas.ClipX * 2;
out_Height = Canvas.ClipY;
return;
case eSST_3P_FAVOR_TOP:
if ( LocalPlayerIndex == 0 )
{
out_Width = Canvas.ClipX;
}
else
{
out_Width = Canvas.ClipX * 2;
}
out_Height = Canvas.ClipY * 2;
return;
case eSST_3P_FAVOR_BOTTOM:
if ( LocalPlayerIndex == 2 )
{
out_Width = Canvas.ClipX;
}
else
{
out_Width = Canvas.ClipX * 2;
}
out_Height = Canvas.ClipY * 2;
return;
case eSST_4P:
out_Width = Canvas.ClipX * 2;
out_Height = Canvas.ClipY * 2;
return;
}
}
/**
* Calculate the amount of safezone needed for a single side for both vertical and horizontal dimensions
*/
final function CalculateSafeZoneValues( out float out_Horizontal, out float out_Vertical, canvas Canvas, int LocalPlayerIndex, bool bUseMaxPercent )
{
local float ScreenWidth, ScreenHeight, XSafeZoneToUse, YSafeZoneToUse;
XSafeZoneToUse = bUseMaxPercent ? TitleSafeZone.MaxPercentX : TitleSafeZone.RecommendedPercentX;
YSafeZoneToUse = bUseMaxPercent ? TitleSafeZone.MaxPercentY : TitleSafeZone.RecommendedPercentY;
GetPixelSizeOfScreen( ScreenWidth, ScreenHeight, Canvas, LocalPlayerIndex );
out_Horizontal = (ScreenWidth * (1 - XSafeZoneToUse) / 2.0f);
out_Vertical = (ScreenHeight * (1 - YSafeZoneToUse) / 2.0);
}
/*
* Return true if the safe zone exists
* pixel size of the deadzone for all sides (right/left/top/bottom) based on which local player it is
*/
final function bool CalculateDeadZoneForAllSides( LocalPlayer LPlayer, Canvas Canvas, out float fTopSafeZone, out float fBottomSafeZone, out float fLeftSafeZone, out float fRightSafeZone, optional bool bUseMaxPercent )
{
// save separate - if the split screen is in bottom right, then
local bool bHasTopSafeZone, bHasBottomSafeZone, bHasRightSafeZone, bHasLeftSafeZone;
local int LocalPlayerIndex;
local float HorizSafeZoneValue, VertSafeZoneValue;
if ( LPlayer != None )
{
LocalPlayerIndex = ConvertLocalPlayerToGamePlayerIndex( LPlayer );
if ( LocalPlayerIndex != -1 )
{
// see if this player should have a safe zone for any particular zonetype
bHasTopSafeZone = HasTopSafeZone( LocalPlayerIndex );
bHasBottomSafeZone = HasBottomSafeZone( LocalPlayerIndex );
bHasLeftSafeZone = HasLeftSafeZone( LocalPlayerIndex );
bHasRightSafeZone = HasRightSafeZone( LocalPlayerIndex );
// if they need a safezone, then calculate it and save it
if ( bHasTopSafeZone || bHasBottomSafeZone || bHasLeftSafeZone || bHasRightSafeZone)
{
// calculate the safezones
CalculateSafeZoneValues( HorizSafeZoneValue, VertSafeZoneValue, Canvas, LocalPlayerIndex, bUseMaxPercent );
if (bHasTopSafeZone)
{
fTopSafeZone = VertSafeZoneValue;
}
else
{
fTopSafeZone = 0.f;
}
if (bHasBottomSafeZone)
{
fBottomSafeZone = VertSafeZoneValue;
}
else
{
fBottomSafeZone = 0.f;
}
if (bHasLeftSafeZone)
{
fLeftSafeZone = HorizSafeZoneValue;
}
else
{
fLeftSafeZone = 0.f;
}
if (bHasRightSafeZone)
{
fRightSafeZone = HorizSafeZoneValue;
}
else
{
fRightSafeZone = 0.f;
}
return TRUE;
}
}
}
return FALSE;
}
/**
* Called every frame to allow the game viewport to update time based state.
* @param DeltaTime - The time since the last call to Tick.
*/
event Tick(float DeltaTime);
/**
* Draw the safe area using the current TitleSafeZone settings
*/
function DrawTitleSafeArea( canvas Canvas )
{
// red colored max safe area box
Canvas.SetDrawColor(255,0,0,255);
Canvas.SetPos(Canvas.ClipX * (1 - TitleSafeZone.MaxPercentX) / 2.0, Canvas.ClipY * (1 - TitleSafeZone.MaxPercentY) / 2.0);
Canvas.DrawBox(Canvas.ClipX * TitleSafeZone.MaxPercentX, Canvas.ClipY * TitleSafeZone.MaxPercentY);
// yellow colored recommended safe area box
Canvas.SetDrawColor(255,255,0,255);
Canvas.SetPos(Canvas.ClipX * (1 - TitleSafeZone.RecommendedPercentX) / 2.0, Canvas.ClipY * (1 - TitleSafeZone.RecommendedPercentY) / 2.0);
Canvas.DrawBox(Canvas.ClipX * TitleSafeZone.RecommendedPercentX, Canvas.ClipY * TitleSafeZone.RecommendedPercentY);
}
/**
* Called after rendering the player views and HUDs to render menus, the console, etc.
* This is the last rendering call in the render loop
* @param Canvas - The canvas to use for rendering.
*/
event PostRender(Canvas Canvas)
{
if( bShowTitleSafeZone )
{
DrawTitleSafeArea(Canvas);
}
if (ViewportConsole != none)
{
// Render the console.
ViewportConsole.PostRender_Console(Canvas);
}
// Draw the transition screen.
DrawTransition(Canvas);
}
/**
* display progress messages in center of screen
*/
function DisplayProgressMessage(Canvas Canvas)
{
local int i, LineCount;
local float FontDX, FontDY;
local float X, Y;
local byte Alpha;
local float TimeLeft;
TimeLeft = ProgressTimeOut - class'Engine'.static.GetCurrentWorldInfo().TimeSeconds;
Alpha = (TimeLeft >= ProgressFadeTime) ? 255 : byte((255 * TimeLeft) / ProgressFadeTime);
LineCount = 0;
for (i = 0; i < ArrayCount(ProgressMessage); i++)
{
if (ProgressMessage[i] != "")
{
LineCount++;
}
}
Canvas.Font = class'Engine'.Static.GetMediumFont();
Canvas.TextSize ("A", FontDX, FontDY);
X = (0.5 * Canvas.SizeX);
Y = (0.5 * Canvas.SizeY);
Y -= FontDY * (float(LineCount) / 2.0);
Canvas.DrawColor.R = 255;
Canvas.DrawColor.G = 255;
Canvas.DrawColor.B = 255;
for (i = 0; i < ArrayCount(ProgressMessage); i++)
{
if (ProgressMessage[i] != "")
{
Canvas.DrawColor.A = Alpha;
Canvas.TextSize(ProgressMessage[i], FontDX, FontDY);
Canvas.SetPos(X - (FontDX / 2.0), Y);
Canvas.DrawText(ProgressMessage[i]);
Y += FontDY;
}
}
}
/**
* Displays the transition screen.
* @param Canvas - The canvas to use for rendering.
*/
function DrawTransition(Canvas Canvas)
{
switch(Outer.TransitionType)
{
case TT_Loading:
DrawTransitionMessage(Canvas,LoadingMessage);
break;
case TT_Saving:
DrawTransitionMessage(Canvas,SavingMessage);
break;
case TT_Connecting:
DrawTransitionMessage(Canvas,ConnectingMessage);
break;
case TT_Precaching:
DrawTransitionMessage(Canvas,PrecachingMessage);
break;
case TT_Paused:
DrawTransitionMessage(Canvas,PausedMessage);
break;
}
}
/**
* Print a centered transition message with a drop shadow.
*/
function DrawTransitionMessage(Canvas Canvas,string Message)
{
local float XL, YL;
Canvas.Font = class'Engine'.Static.GetLargeFont();
Canvas.bCenter = false;
Canvas.StrLen( Message, XL, YL );
Canvas.SetPos(0.5 * (Canvas.ClipX - XL) + 1, 0.66 * Canvas.ClipY - YL * 0.5 + 1);
Canvas.SetDrawColor(0,0,0);
Canvas.DrawText( Message, false );
Canvas.SetPos(0.5 * (Canvas.ClipX - XL), 0.66 * Canvas.ClipY - YL * 0.5);
Canvas.SetDrawColor(0,0,255);;
Canvas.DrawText( Message, false );
}
/**
* Notifies all interactions that a new player has been added to the list of active players.
*
* @param PlayerIndex the index [into the GamePlayers array] where the player was inserted
* @param AddedPlayer the player that was added
*/
final function NotifyPlayerAdded( int PlayerIndex, LocalPlayer AddedPlayer )
{
local int InteractionIndex;
LayoutPlayers();
for ( InteractionIndex = 0; InteractionIndex < GlobalInteractions.Length; InteractionIndex++ )
{
if ( GlobalInteractions[InteractionIndex] != None )
{
GlobalInteractions[InteractionIndex].NotifyPlayerAdded(PlayerIndex, AddedPlayer);
}
}
}
/**
* Notifies all interactions that a new player has been added to the list of active players.
*
* @param PlayerIndex the index [into the GamePlayers array] where the player was located
* @param RemovedPlayer the player that was removed
*/
final function NotifyPlayerRemoved( int PlayerIndex, LocalPlayer RemovedPlayer )
{
local int InteractionIndex;
LayoutPlayers();
for ( InteractionIndex = GlobalInteractions.Length - 1; InteractionIndex >= 0; InteractionIndex-- )
{
if ( GlobalInteractions[InteractionIndex] != None )
{
GlobalInteractions[InteractionIndex].NotifyPlayerRemoved(PlayerIndex, RemovedPlayer);
}
}
}
/**
* Adds a LocalPlayer to the local and global list of Players.
*
* @param NewPlayer the player to add
*/
private final function int AddLocalPlayer( LocalPlayer NewPlayer )
{
local int InsertIndex;
InsertIndex = INDEX_NONE;
if ( NewPlayer != None )
{
// add to list
InsertIndex = GamePlayers.Length;
GamePlayers[InsertIndex] = NewPlayer;
}
return InsertIndex;
}
/**
* Removes a LocalPlayer from the local and global list of Players.
*
* @param ExistingPlayer the player to remove
*/
private final function int RemoveLocalPlayer( LocalPlayer ExistingPlayer )
{
local int Index;
Index = GamePlayers.Find(ExistingPlayer);
if ( Index != INDEX_NONE )
{
GamePlayers.Remove(Index,1);
}
return Index;
}
/** handler for global state messages, generally network connection related (failures, download progress, etc) */
event SetProgressMessage(EProgressMessageType MessageType, string Message, optional string Title, optional bool bIgnoreFutureNetworkMessages)
{
local WorldInfo WI;
WI = class'Engine'.static.GetCurrentWorldInfo();
if (MessageType == PMT_Clear)
{
ClearProgressMessages();
}
else
{
if (MessageType == PMT_ConnectionFailure || MessageType == PMT_SocketFailure)
{
// Attempt to start host migration on connection failure
if (WI != None &&
WI.NetMode == NM_Client &&
WI.BeginHostMigration())
{
`Log(`location @ "MessageType="$MessageType @ "Message="$Message @ ": host migration started.. "$WI.PeerHostMigration.HostMigrationProgress,,'DevNet');
}
//@FIXME: bIgnoreNetworkMessages needs to die
else if (!Outer.GamePlayers[0].Actor.bIgnoreNetworkMessages)
{
`Log(`location @ "MessageType="$MessageType @ "Message="$Message @ ": host migration not enabled.. handling connection error.",,'DevNet');
NotifyConnectionError(MessageType, Message, Title);
}
}
else if (MessageType == PMT_PeerHostMigrationFailure)
{
// Host migration was started but failed so just fallback to network error handling
Outer.GamePlayers[0].Actor.bIgnoreNetworkMessages = false;
NotifyConnectionError(MessageType, Message, Title);
}
else
{
if (Title != "")
{
ProgressMessage[0] = Title;
ProgressMessage[1] = Message;
}
else
{
ProgressMessage[1] = "";
ProgressMessage[0] = Message;
}
}
}
//@FIXME: bIgnoreNetworkMessages needs to die
if (!Outer.GamePlayers[0].Actor.bIgnoreNetworkMessages)
{
Outer.GamePlayers[0].Actor.bIgnoreNetworkMessages = bIgnoreFutureNetworkMessages;
}
}
/**
* Notifies the player that an attempt to connect to a remote server failed, or an existing connection was dropped.
*
* @param MessageType EProgressMessageType of current connection error
* @param Message a description of why the connection was lost
* @param Title the title to use in the connection failure message.
*/
function NotifyConnectionError(EProgressMessageType MessageType, optional string Message=Localize("Errors", "ConnectionFailed", "Engine"), optional string Title=Localize("Errors", "ConnectionFailed_Title", "Engine") )
{
local WorldInfo WI;
WI = class'Engine'.static.GetCurrentWorldInfo();
`log(`location @ `showvar(Title) @ `showvar(Message) @ `showenum(ENetMode,WI.NetMode,NetMode) @ `showvar(WI.GetURLMap(),Map) ,,'DevNet');
if (WI.NetMode != NM_Standalone)
{
if ( WI.Game != None )
{
// Mark the server as having a problem
WI.Game.bHasNetworkError = true;
}
//@todo: should we have a Travel() function in this class?
ConsoleCommand("start ?failed");
}
}
exec event SetProgressTime(float T)
{
ProgressTimeOut = T + class'Engine'.static.GetCurrentWorldInfo().TimeSeconds;
}
exec function ClearProgressMessages()
{
local int i;
for (i=0; i<ArrayCount(ProgressMessage); i++)
{
ProgressMessage[i] = "";
}
}
/**
* Retrieves a reference to a LocalPlayer.
*
* @param PlayerIndex if specified, returns the player at this index in the GamePlayers array. Otherwise, returns
* the player associated with the owner scene.
*
* @return the player that owns this scene or is located in the specified index of the GamePlayers array.
*/
native final function LocalPlayer GetPlayerOwner(int PlayerIndex);
/** Called after the primary player has been changed so that the UI references to the owner are switched */
native final function FixupOwnerReferences(array<int> IDMappings);
/** Called after a new primary player has been made */
function OnPrimaryPlayerSwitch(LocalPlayer OldPrimaryPlayer, LocalPlayer NewPrimaryPlayer);
/**
* Makes a player the primary player
* @param PlayerIndex - The index of the player to be made into the primary player
*/
function BecomePrimaryPlayer(int PlayerIndex)
{
local array<LocalPlayer> OtherPlayers;
local LocalPlayer PlayerOwner, NextPlayer, OriginalPrimaryPlayer;
local int NumPlayersRemoved;
local int i, count;
//Mapping of: index into array is new index, value is old index
local array<int> IDMappings;
if (UIController != None && PlayerIndex > 0 && PlayerIndex < UIController.GetPlayerCount())
{
OriginalPrimaryPlayer = GetPlayerOwner(0);
// get the player that owns this scene
PlayerOwner = GetPlayerOwner(PlayerIndex);
if (PlayerOwner == None)
{
`log("GameViewportClient:BecomePrimaryPlayer has failed to find the player owner for index" @ PlayerIndex @ "ABORTING!!!");
return;
}
if (PlayerOwner != None)
{
NextPlayer = OriginalPrimaryPlayer;
NumPlayersRemoved = 0;
while (NextPlayer != None && NextPlayer != PlayerOwner)
{
// the easiest way to ensure that everything is updated properly is to simulate the player being removed;
// do it manually so that their PlayerController and stuff aren't destroyed.
UIController.NotifyPlayerRemoved(0, NextPlayer);
UIController.Outer.Outer.GamePlayers.Remove(0, 1);
// we need to re-add the player so keep them in a temporary list
OtherPlayers.AddItem(NextPlayer);
NextPlayer = GetPlayerOwner(0);
NumPlayersRemoved++;
}
//Update mapping for all movies that just got shifted downwards
for (i = 0; i < UIController.Outer.Outer.GamePlayers.length; i++)
{
IDMappings.AddItem(i+NumPlayersRemoved);
}
// now re-add the previous players to the GamePlayers array.
count = 0;
while (OtherPlayers.Length > 0)
{
NextPlayer = OtherPlayers[0];
UIController.Outer.Outer.GamePlayers.AddItem(NextPlayer);
UIController.NotifyPlayerAdded(UIController.Outer.Outer.GamePlayers.length-1, NextPlayer);
OtherPlayers.Remove(0, 1);
IDMappings.AddItem(count);
count++;
}
}
// if we have a new primary player, reload their profile so that their settings will be applied and fixup references
NextPlayer = GetPlayerOwner(0);
if (OriginalPrimaryPlayer != NextPlayer)
{
// Players switched so reevaluate the viewports
LayoutPlayers();
FixupOwnerReferences(IDMappings);
NextPlayer.Actor.ReloadProfileSettings();
OnPrimaryPlayerSwitch(OriginalPrimaryPlayer, NextPlayer);
}
}
}
/** Function to enable Scaleform processing/rendering */
native final function EnableScaleform();
/** Function to disable Scaleform processing/rendering */
native final function DisableScaleform();
/** Function to find out if Scaleform is enabled/disabled */
native final function bool IsScaleformEnabled();
/** DEBUG: function to easily allow script to turn on / off the two UI systems for developing during the transition from the old UI to the new GFx UI */
native function DebugSetUISystemEnabled(bool bOldUISystemActive, bool bGFxUISystemActive);
/**
* Function to set the hardware mouse cursor visibility
*
* @param bIsVisible - whether or not the cursor is visible
*/
simulated event SetHardwareMouseCursorVisibility(bool bIsVisible)
{
local Vector2D ViewportSize;
//If we are going to be turning on the hardware cursor when it was not already on, we will move the cursor to the middle of the screen
if (bIsVisible && !bDisplayHardwareMouseCursor)
{
GetViewportSize(ViewportSize);
SetMouse(ViewportSize.X/2,ViewportSize.Y/2);
}
bDisplayHardwareMouseCursor = bIsVisible;
ForceUpdateMouseCursor(TRUE);
}
defaultproperties
{
UIControllerClass=class'Engine.UIInteraction'
TitleSafeZone=(MaxPercentX=0.9,MaxPercentY=0.9,RecommendedPercentX=0.8,RecommendedPercentY=0.8)
Default2PSplitType=eSST_2P_HORIZONTAL
Default3PSplitType=eSST_3P_FAVOR_TOP
DesiredSplitscreenType=eSST_NONE
bIsPlayInEditorViewport=False
bShowSystemMouseCursor=False
ProgressFadeTime=1.0
ProgressTimeOut=8.0
SplitscreenInfo(eSST_None)= (PlayerData=((SizeX=1.0f,SizeY=1.0f,OriginX=0.0f,OriginY=0.0f)))
SplitscreenInfo(eSST_2P_HORIZONTAL)={(PlayerData=(
(SizeX=1.0f,SizeY=0.5f,OriginX=0.0f,OriginY=0.0f),
(SizeX=1.0f,SizeY=0.5f,OriginX=0.0f,OriginY=0.5f))
)}
SplitscreenInfo(eSST_2P_VERTICAL)={(PlayerData=(
(SizeX=0.5f,SizeY=1.0f,OriginX=0.0f,OriginY=0.0f),
(SizeX=0.5f,SizeY=1.0f,OriginX=0.5f,OriginY=0.0))
)}
SplitscreenInfo(eSST_3P_FAVOR_TOP)={(PlayerData=(
(SizeX=1.0f,SizeY=0.5f,OriginX=0.0f,OriginY=0.0f),
(SizeX=0.5f,SizeY=0.5f,OriginX=0.0f,OriginY=0.5f),
(SizeX=0.5f,SizeY=0.5f,OriginX=0.5f,OriginY=0.5f))
)}
SplitscreenInfo(eSST_3P_FAVOR_BOTTOM)={(PlayerData=(
(SizeX=0.5f,SizeY=0.5f,OriginX=0.0f,OriginY=0.0f),
(SizeX=0.5f,SizeY=0.5f,OriginX=0.5f,OriginY=0.0f),
(SizeX=1.0f,SizeY=0.5f,OriginX=0.0f,OriginY=0.5f))
)}
SplitscreenInfo(eSST_4P)={(PlayerData=(
(SizeX=0.5f,SizeY=0.5f,OriginX=0.0f,OriginY=0.0f),
(SizeX=0.5f,SizeY=0.5f,OriginX=0.5f,OriginY=0.0f),
(SizeX=0.5f,SizeY=0.5f,OriginX=0.0f,OriginY=0.5f),
(SizeX=0.5f,SizeY=0.5f,OriginX=0.5f,OriginY=0.5f))
)}
}