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

1539 lines
50 KiB
Ucode

// ====================================================================
// Class: Engine.GUIController
//
// The GUIController is a simple FILO menu stack. You have 3 things
// you can do. You can Open a menu which adds the menu to the top of the
// stack. You can Replace a menu which replaces the current menu with the
// new menu. And you can close a menu, which returns you to the last menu
// on the stack.
//
// Written by Joe Wilcox
// (c) 2002, Epic Games, Inc. All Rights Reserved
// ====================================================================
class GUIController extends BaseGUIController
DependsOn(GUI)
Config(User)
Abstract
Native;
// if _RO_
// else
//#exec OBJ LOAD FILE=InterfaceContent.utx
// end if _RO_
cpptext
{
void GetCursorPos( INT& ptX, INT& ptY );
void NativeMessage(const FString Msg, FLOAT MsgLife);
UBOOL NativeKeyType(BYTE& iKey, TCHAR Unicode );
UBOOL NativeKeyEvent(BYTE& iKey, BYTE& State, FLOAT Delta );
void NativeTick(FLOAT DeltaTime);
void NativePreRender(UCanvas* Canvas);
void NativePostRender(UCanvas* Canvas);
void ResolutionChanged( INT NewX, INT NewY );
void ResetInput();
UBOOL DesignKeyEvent( BYTE iKey, BYTE State, FLOAT Delta );
void RenderDesignMode( UCanvas* Canvas );
virtual void LookUnderCursor(FLOAT dX, FLOAT dY);
UGUIComponent* UnderCursor(FLOAT MouseX, FLOAT MouseY);
UBOOL virtual MousePressed(UBOOL IsRepeat);
UBOOL virtual MouseReleased();
UBOOL HasMouseMoved( FLOAT ErrorMargin = 0.f );
void PlayInterfaceSound(USound* sound);
void PlayComponentSound(BYTE SoundNum);
void Profile(FString& ProfileName);
void Destroy();
// Design mode
void CreatePropertyWindow();
void HidePropertyWindow();
void SetMoveControl( UGUIComponent* C );
UBOOL DesignerVisible();
UBOOL ShowProperties( UGUIComponent* Comp );
void ResetDesigner();
}
const DoCounter = 1;
var const int FONT_NUM;
var const int STYLE_NUM;
var const int CURSOR_NUM;
struct native ProfileStruct
{
var string ProfileName;
var float ProfileSeconds;
};
struct native eOwnageMap
{
var int RLevel;
var string MapName;
var string MapDesc;
var string MapURL;
};
var array<ProfileStruct> Profilers;
var const floatbox MouseCursorBounds;
var editinline Array<vector> MouseCursorOffset; // Only X,Y used, between 0 and 1. 'Hot Spot' of cursor material.
var editinline export protected array<GUIPage> MenuStack; // Holds the stack of menus
var protected Array<GUIPage> PersistentStack; // Holds the set of pages which are persistent across close/open
var editinline protected Array<GUIFont> FontStack; // Holds all the possible fonts
var protected Array<GUIStyles> StyleStack; // Holds all of the possible styles
var editinline protected Array<Material> MouseCursors; // Holds a list of all possible mouse
var array<material> ImageList; // List of various images
struct native AutoLoadMenu
{
var string MenuClassName;
var bool bPreInitialize;
};
struct native init RestoreMenuItem
{
var() string MenuClassName;
var() string Param1, Param2;
};
var Array<string> DefaultStyleNames; // Holds the name of all styles to use
var Array<string> StyleNames; // Holds the name of all styles to use
var config array<AutoLoadMenu> AutoLoad; // Any menu classes in here will be automatically loaded
var array<RestoreMenuItem> RestoreMenus;
struct native DesignModeHint
{
var() localized string Key, Description;
};
var array<DesignModeHint> DesignModeHints; // List of commands for design mode
var config float MenuMouseSens;
var float MouseX,MouseY; // Where is the mouse currently located
var float LastMouseX, LastMouseY;
var float DblClickWindow; // How long do you have for a double click
var float LastClickTime; // When did the last click occur
var float ButtonRepeatDelay; // The amount of delay for faking button repeats
var float RepeatDelta; // Data var
var float RepeatTime; // How long until the next repeat;
var float CursorFade; // How visible is the cursor
var float FastCursorFade; // How visible is the cursor
var globalconfig int MaxSimultaneousPings; // Upper limit to number of open connections (for server pinging only - not does include game channels)
//ifdef _KF_
var globalconfig int MaxPingsPerSecond;
//endif _KF_
var int FastCursorStep; // Are we fading in or out
var const int ResX, ResY; // Current resolution
var int LastClickX, LastClickY; // Who was the active component
var int CursorStep; // Are we fading in or out
var const private pointer Designer; // GUI design editor window reference
// Sounds
var sound MouseOverSound;
var sound ClickSound;
var sound EditSound;
var sound UpSound;
var sound DownSound;
var sound DragSound;
var sound FadeSound;
var GUIPage ActivePage; // Points to the currently active page
var GUIComponent FocusedControl; // Top most Focused control
var GUIComponent ActiveControl; // Which control is currently active
var deprecated GUIComponent SkipControl; // This control should be skipped over and drawn at the end
var GUIComponent MoveControl; // Used for visual design
// Drag - n - Drop support
var GUIComponent DropSource; // Source component for a drag-n-drop operation currently in progress
var GUIComponent DropTarget; // Target component for a drag-n-drop operation currently in progress
var GUIContextMenu ContextMenu; // Used for Right Click menus
var GUIToolTip MouseOver;
var Material WhiteBorder;
var string GameResolution;
var transient string LastGameType; // Used for some places where we need to know the last gametype selected
// Menu types
var config string RequestDataMenu; // Menu for single item data entry
var config string ArrayPropertyMenu; // Menu for editing static array playinfo properties
var config string DynArrayPropertyMenu; // Menu for editing dynamic array playinfo properties
// ifdef _KF_
var string FilterMenu; // Menu for configuring server browser filters
// else
//var config string FilterMenu; // Menu for configuring server browser filters
// endif _KF_
var config string MapVotingMenu; // Menu that appears for map voting
var config string KickVotingMenu;
var config string MatchSetupMenu;
var config string EditFavoriteMenu;
var array<string> MainMenuOptions; // Menu classes that will be displayed on the main menu
var globalconfig string DesignerMenu; // Menu for in-game GUI designer
var byte RepeatKey; // Used to determine what should repeat
var bool bIgnoreNextRelease; // Used to make sure discard errant releases.
var bool ShiftPressed; // Shift key is being held
var bool AltPressed; // Alt key is being held
var bool CtrlPressed; // Ctrl key is being held
var bool bModulateStackedMenus; // If true, all menus except the topmost will be greyed
var globalconfig bool bQuietMenu;
var globalconfig bool bNoToolTips; // Do not display mouse-over tool-tips
var globalconfig bool bDesignModeToolTips; // Whether tooltips are displayed while in design mode
var globalconfig bool bAutoRefreshBrowser; // Auto refresh server browser when returning from the game or another menu
var globalconfig bool bModAuthor; // Allows bDesign Mode
var globalconfig bool bExpertMode; // Display all advanced settings
var globalconfig bool bDesignMode; // Are we in design mode;
var bool bInteractiveMode; // All input goes to design mode
var globalconfig bool bHighlightCurrent; // Highlight the current control being edited
var globalconfig bool bDrawFullPaths; // Display full menu paths in design mode
var bool MainNotWanted; // Prevents main menu from being opened when last menu is closed and isn't bAllowedAsLast
var bool bCurMenuInitialized; // Has the current Menu Finished initialization
var bool bForceMouseCheck; // HACK
var bool bIgnoreUntilPress; // HACK //???
var bool bSnapCursor; // Snap cursor to first component when page is opened
var float RenderDelta, LastRenderTime; // Used for timing
var config bool bFixedMouseSize; // Keep the mouse from scaling
var const byte KeyDown[255]; // Bit mask of keys currently held down
var array<class<GUIComponent> > RegisteredClasses;
var bool bECEEdition; // Set to true if this is the ECE edition
// G15 Support
var texture LCDLogo;
var font LCDTinyFont;
var font LCDSmallFont;
var font LCDMedFont;
var font LCDLargeFont;
// end G15 support
delegate bool OnNeedRawKeyPress(byte NewKey);
delegate AddBuddy( optional string NewBuddyName );
// =====================================================================================================================
// =====================================================================================================================
// Utility functions for the UI
// =====================================================================================================================
// =====================================================================================================================
native event GUIFont GetMenuFont(string FontName); // Finds a given font in the FontStack
native event GUIStyles GetStyle(string StyleName, out GUI.eFontScale FontScale); // Find a style on the stack
native final function string GetCurrentRes(); // Returns the current res as a string
// Attempts to set the render device to the class specified by NewRenderDevice - returns whether change was successful
native final function bool SetRenderDevice( string NewRenderDevice );
native private final function ResetDesigner();
native final function ResetInput();
native final function GetProfileList(string Prefix, out array<string> ProfileList);
native final function ResetKeyboard();
native final function GetOGGList(out array<string> OGGFiles);
native final function PlayInterfaceSound( GUIComponent.EClickSound SoundType );
native final function SetMoveControl( GUIComponent C );
native function Profile(string ProfileName);
// Used by a demo manager
native function GetDEMList(out array<string> DEMFiles);
native function bool GetDEMHeader(string DemoName, out string MapName, out string GameType,
out int ScoreLimit, out int TimeLimit, out int ClientSide,
out string RecordedBy, out string Timestamp, out String ReqPackages);
// Used by Ownage Maps
native function GetOwnageList(out array<int> RLevel, out array<string> MNames, out array<string> MDesc, out array<string> mURL);
native function SaveOwnageList(array<eOwnageMap> Maps);
// deprecated -- Use CacheManager/xUtil instead
native final function GetWeaponList(out array<class<Weapon> > WeaponClass, out array<string> WeaponDesc);
native final function GetMapList( string Prefix, GUIList list, optional bool bDecoText );
// URL stuff
native function LaunchURL(string URL);
// Firewall stuff
native function bool CheckFirewall();
native function bool AuthroizeFirewall();
native function bool CheckForECE();
//@ G15 Support
// LCD Functions
native function bool bLCDAvailable();
native function LCDDrawTile(Texture Tex, int X, int Y, int XL, int YL, int U, int V, int UL, int VL);
native function LCDDrawText(string Text, int X, int Y, Font Font);
native function LCDStrLen(string Text, Font Font, out int XL, out int YL);
native function LCDCls();
native function LCDRepaint();
native function bool CheckSteam();
// Steam interface functions
// _RO
native function bool SteamRefreshLogin(string Password);
native function string SteamGetUserName();
native function string SteamGetUserID();
simulated event ResolutionChanged(){}
// end _RO_
function string LoadDecoText(string PackageName, string DecoTextName)
{
local int i;
local DecoText Deco;
local string DecoText;
if (InStr(DecoTextName, ".") != -1)
{
if (PackageName == "")
Divide(DecoTextName, ".", PackageName, DecoTextName);
else DecoTextName = Mid(DecoTextName, InStr(DecoTextName, ".") + 1);
}
Deco = class'xUtil'.static.LoadDecoText(PackageName, DecoTextName);
if (Deco == None)
return "";
for (i = 0; i < Deco.Rows.Length; i++)
{
if (DecoText != "")
DecoText $= "|";
DecoText $= Deco.Rows[i];
}
return DecoText;
}
final function GetTeamSymbolList(out array<string> SymbolNames, optional bool bNoSinglePlayer)
{
// Moved to CacheManager so GameInfo can access this
class'CacheManager'.static.GetTeamSymbolList(SymbolNames, bNoSinglePlayer);
}
// -- deprecated
function GUIPage TopPage()
{
return ActivePage;
}
// =====================================================================================================================
// =====================================================================================================================
// Initialization
// =====================================================================================================================
// =====================================================================================================================
event InitializeController()
{
local int i;
local class<GUIStyles> NewStyleClass;
LCDDrawTile(LCDLogo,0,0,64,43,0,0,64,43);
LCDDrawText("Loading...",55,10,LCDMedFont);
LCDDrawText("Killing Floor",55,26,LCDMedFont);
LCDRePaint();
PrecachePlayerRecords();
for (i=0;i<DefaultStyleNames.Length;i++)
{
NewStyleClass = class<GUIStyles>(DynamicLoadObject(DefaultStyleNames[i],class'class'));
if (NewStyleClass != None)
if (!RegisterStyle(NewStyleClass))
log("Could not create requested style"@DefaultStyleNames[i]);
}
for (i=0;i<StyleNames.Length;i++)
{
NewStyleClass = class<GUIStyles>(DynamicLoadObject(StyleNames[i],class'class'));
if (NewStyleClass != None)
if (!RegisterStyle(NewStyleClass))
log("Could not create requested style"@StyleNames[i]);
}
for (i=0;i<FontStack.length;i++)
FontStack[i].Controller = self;
class'CacheManager'.static.InitCache();
bECEEdition = CheckForECE();
}
function PrecachePlayerRecords()
{
local xUtil.PlayerRecord Rec;
Rec = class'xUtil'.static.GetPlayerRecord(0);
}
function bool RegisterStyle(class<GUIStyles> StyleClass, optional bool bTemporary)
{
local GUIStyles NewStyle;
local int i,index;
Index = -1;
if (StyleClass != None)
{
for (i = 0; i < StyleStack.Length; i++)
{
if (StyleStack[i].Class == StyleClass)
{
log("Style already registered '"$StyleClass$"'");
return true;
}
if (StyleStack[i].KeyName == StyleClass.Default.KeyName)
Index = i;
}
NewStyle = new(None) StyleClass;
if (NewStyle != None)
{
// Dynamic Array Auto Sizes StyleStack.
if (Index<0)
{
// if ( bModAuthor )
// log("Registering Style"@NewStyle,'ModAuthor');
StyleStack[StyleStack.Length] = NewStyle;
}
else
{
if ( bModAuthor )
log("Replacing Style"@StyleStack[Index].KeyName@"with"@NewStyle,'ModAuthor');
StyleStack[Index].Controller = none;
StyleStack[Index] = NewStyle;
}
NewStyle.Controller = self;
NewStyle.Initialize();
NewStyle.bTemporary = bTemporary;
return true;
}
}
return false;
}
//event class<GUIComponent> AddComponentClass(string ClassName) { return None; }
event class<GUIComponent> AddComponentClass(string ClassName)
{
local int i;
local class<GUIComponent> Cls;
for ( i = 0; i < RegisteredClasses.Length; i++ )
if ( string(RegisteredClasses[i]) ~= ClassName )
return RegisteredClasses[i];
Cls = class<GUIComponent>(DynamicLoadObject(ClassName,class'Class'));
if ( Cls != None )
RegisteredClasses[RegisteredClasses.Length] = Cls;
return Cls;
}
// =====================================================================================================================
// =====================================================================================================================
// Menu manipulation
// =====================================================================================================================
// =====================================================================================================================
event GUIPage FindPersistentMenuByName( string MenuClass )
{
local int i;
if ( MenuClass == "" )
return None;
for ( i = 0; i < PersistentStack.Length; i++ )
if ( MenuClass ~= string(PersistentStack[i].Class) )
return PersistentStack[i];
return None;
}
event int FindMenuIndexByName( string MenuClass )
{
local int i;
if ( MenuClass == "" )
return -1;
for ( i = 0; i < MenuStack.Length; i++ )
if ( MenuClass ~= string(MenuStack[i].Class) )
return i;
return -1;
}
event int FindMenuIndex( GUIPage Menu )
{
local int i;
if ( Menu == None )
return -1;
for ( i = 0; i < MenuStack.Length; i++ )
if ( MenuStack[i] == Menu )
return i;
return -1;
}
event int FindPersistentMenuIndex( GUIPage Menu )
{
local int i;
if ( Menu == None )
return -1;
for ( i = 0; i < PersistentStack.Length; i++ )
if ( Menu == PersistentStack[i] )
return i;
return -1;
}
function GUIPage FindPersistentMenuByClass( class<GUIPage> PageClass )
{
local int i;
if ( PageClass == None )
return None;
for ( i = 0; i < PersistentStack.Length; i++ )
if ( ClassIsChildOf(PersistentStack[i].Class, PageClass) )
return PersistentStack[i];
return None;
}
function GUIPage FindMenuByClass( class<GUIPage> PageClass )
{
local int i;
if ( PageClass == None )
return None;
for ( i = 0; i < MenuStack.Length; i++ )
if ( ClassIsChildOf(MenuStack[i].Class,PageClass) )
return MenuStack[i];
return None;
}
// CreateMenu - Attempts to Create a menu. Returns none if it can't
event GUIPage CreateMenu(string NewMenuName)
{
local class<GUIPage> NewMenuClass;
local GUIPage NewMenu;
local int i;
// Big hack here - If the menu we are creating is XInterface.UT2MainMenu then
// Pull the actual main menu from native code
if (NewMenuName ~= "XInterface.UT2MainMenu")
NewMenuName = GetMainMenuClass();
// Load the menu's package if needed
NewMenuClass = class<GUIPage>(AddComponentClass(NewMenuName));
if (NewMenuClass != None)
{
// If it's persistent, try to find an instance in the PersistentStack.
if( NewMenuClass.default.bPersistent )
{
for( i=0;i<PersistentStack.Length;i++ )
{
if( PersistentStack[i].Class == NewMenuClass )
{
NewMenu = PersistentStack[i];
break;
}
}
}
// Not found, spawn a new menu
if( NewMenu == None )
{
NewMenu = new(None) NewMenuClass;
// Check for errors
if (NewMenu == None)
{
log("Could not create requested menu"@NewMenuName);
return None;
}
else
if( NewMenuClass.default.bPersistent )
{
// Save in PersistentStack if it's persistent.
i = PersistentStack.Length;
PersistentStack.Length = i+1;
PersistentStack[i] = NewMenu;
}
}
bCurMenuInitialized = false;
return NewMenu;
}
log("Could not DLO menu '"$NewMenuName$"'");
return none;
}
// ================================================
// OpenMenu - Opens a new menu and places it on top of the stack
event bool OpenMenu(string NewMenuName, optional string Param1, optional string Param2)
{
local GUIPage NewMenu;
// Sanity Check
if ( bModAuthor )
log(Class@"OpenMenu ["$NewMenuName$"] ("$Param1$") ("$Param2$")",'ModAuthor');
if (ActivePage != none)
{
if ( !ActivePage.AllowOpen(NewMenuName) )
return false;
}
if ( !bCurMenuInitialized && MenuStack.Length > 0 )
{
if ( bModAuthor )
log("Cannot open menu until menu initialization is complete!",'ModAuthor');
return false;
}
NewMenu = CreateMenu(NewMenuName);
if (NewMenu!=None)
{
// do not allow the same menu to be duplicated in the stack
if ( FindMenuIndex(NewMenu) != -1 )
{
bCurMenuInitialized = True;
return false;
}
NewMenu.ParentPage = ActivePage;
ResetFocus();
PushMenu( MenuStack.Length, NewMenu, Param1, Param2 );
if ( NewMenu.bDisconnectOnOpen )
ConsoleCommand("DISCONNECT");
return true;
}
log("Could not open menu"@NewMenuName);
return false;
}
event AutoLoadMenus()
{
local GUIPage NewMenu;
local int i;
super.AutoLoadMenus();
for ( i=0; i < AutoLoad.Length; i++ )
{
NewMenu = CreateMenu(AutoLoad[i].MenuClassName);
if ( NewMenu == None )
{
log("Could not auto-load"@AutoLoad[i].MenuClassName);
continue;
}
if ( AutoLoad[i].bPreInitialize )
NewMenu.InitComponent(Self, None);
}
}
// ================================================
// Replaces a menu in the stack. returns true if success
event bool ReplaceMenu(string NewMenuName, optional string Param1, optional string Param2, optional bool bCancelled)
{
local GUIPage NewMenu;
if ( ActivePage == None || MenuStack.Length == 0 )
return OpenMenu(NewMenuName, Param1, Param2);
if ( bModAuthor )
log(Class@"ReplaceMenu ["$NewMenuName$"] ("$Param1$") ("$Param2$")",'ModAuthor');
if ( !bCurMenuInitialized && MenuStack.Length > 0 )
{
if ( bModAuthor )
log("Cannot replace menu until menu initialization is complete!",'ModAuthor');
return false;
}
NewMenu = CreateMenu(NewMenuName);
if (NewMenu != None)
{
NewMenu.ParentPage = ActivePage.ParentPage;
ResetFocus();
// Remove the old menu
PopMenu( -1, ActivePage, bCancelled );
// Add this menu to the stack and give it focus
PushMenu( MenuStack.Length - 1, NewMenu, Param1, Param2 );
return true;
}
return false;
}
// Initialize a new menu and add to the stack at the specified point.
protected event PushMenu( int Index, GUIPage NewMenu, optional string Param1, optional string Param2 )
{
if ( NewMenu == None )
{
log("Call to GUIController.PushMenu() with invalid NewMenu!!!");
return;
}
SetControllerStatus(true);
if ( Index >= 0 )
{
MenuStack[Index] = NewMenu;
ActivePage = NewMenu;
}
CloseOverlays();
ResetInput();
if (NewMenu.Controller == None)
NewMenu.InitComponent(Self, None);
NewMenu.CheckResolution(false,Self);
// Pass along the event
NewMenu.Opened(NewMenu);
NewMenu.MenuState = MSAT_Focused;
NewMenu.PlayOpenSound();
bCurMenuInitialized = true;
NewMenu.HandleParameters(Param1, Param2);
bForceMouseCheck = true;
NewMenu.OnOpen();
}
protected event PopMenu( int Index, GUIPage CurMenu, optional bool bCancelled )
{
CloseOverlays();
// Close out the current page
CurMenu.Closed( CurMenu, bCancelled );
if ( Index >= 0 && Index + 1 < MenuStack.Length && MenuStack[Index + 1] != None )
MenuStack[Index + 1].ParentPage = CurMenu.ParentPage;
CurMenu.ParentPage = None;
// Free all object references
CurMenu.Free();
// Sanity check - something may have changed the order of the menustack between
// now and the original call to CloseMenu()/RemoveMenu()
if ( Index < 0 )
return;
if ( Index >= MenuStack.Length || MenuStack[Index] != CurMenu )
Index = FindMenuIndex(CurMenu);
if ( Index >= 0 && Index < MenuStack.Length )
MenuStack.Remove( Index, 1 );
}
function bool RemoveMenuAt( int Index, optional bool bCancelled )
{
if ( Index < 0 || Index >= MenuStack.Length )
return False;
return RemoveMenu(MenuStack[Index], bCancelled);
}
// Close a menu that isn't the top menu
event bool RemoveMenu( GUIPage Menu, optional bool bCancelled )
{
if ( MenuStack.Length == 0 )
{
log("GUIController.RemoveMenu() - Attempting to close a non-existing menu page");
return false;
}
if ( Menu == None || Menu == ActivePage )
return CloseMenu(bCancelled);
if ( bModAuthor )
log(Class@"RemoveMenu ["$Menu.Name$"]",'ModAuthor');
if ( !bCurMenuInitialized )
{
if ( bModAuthor )
log("Cannot remove menu until initialization is complete!",'ModAuthor');
return false;
}
if ( !Menu.OnCanClose(bCancelled) )
return false;
ResetInput();
Menu.PlayCloseSound(); // Play the closing sound
PopMenu( FindMenuIndex(Menu), Menu, bCancelled );
// Gab the next page on the stack
if ( MenuStack.Length == 0 ) // Pass control back to the previous menu
{
if ( !Menu.bAllowedAsLast && !MainNotWanted )
return OpenMenu(GetMainMenuClass());
ActivePage = None;
SetControllerStatus(false);
}
VerifyStack();
bForceMouseCheck = true;
return true;
}
event bool CloseMenu( optional bool bCancelled ) // Close the top menu. returns true if success.
{
if ( MenuStack.Length == 0 || ActivePage == None )
{
log("Attempting to close a non-existing menu page");
return false;
}
if ( bModAuthor )
log(Class@"CloseMenu ["$ActivePage.Name$"]",'ModAuthor');
if ( !bCurMenuInitialized )
{
if ( bModAuthor )
log("Cannot close menu until initialization is complete!",'ModAuthor');
return false;
}
if ( !ActivePage.OnCanClose(bCancelled) )
return false;
ResetInput();
ActivePage.PlayCloseSound(); // Play the closing sound
PopMenu( FindMenuIndex(ActivePage), ActivePage, bCancelled );
ActivePage.CheckResolution(True,Self);
// Gab the next page on the stack
bCurMenuInitialized=false;
if ( MenuStack.Length > 0 ) // Pass control back to the previous menu
{
ActivePage = MenuStack[MenuStack.Length-1];
ActivePage.MenuState = MSAT_Focused;
ResetFocus();
ActivePage.FocusFirst(None);
if ( !ActivePage.bNeverFocus )
ActivePage.OnActivate();
}
else
{
if (!ActivePage.bAllowedAsLast && !MainNotWanted)
{
ActivePage = none;
return OpenMenu(GetMainMenuClass());
}
ActivePage = None;
SetControllerStatus(false);
}
bCurMenuInitialized=true;
if ( ActivePage != None )
ActivePage.OnReopen();
bForceMouseCheck = true;
return true;
}
event CloseAll(bool bCancel, optional bool bForced)
{
local int i;
if ( bModAuthor )
{
log(Name@"CloseAll bCancel:"$bCancel@"Forced:"$bForced@"(Currently"@MenuStack.Length@"menus open)",'ModAuthor');
for ( i = 0; i < MenuStack.Length; i++ )
{
if ( MenuStack[i] == None )
log(" Menu["$i$"]: None",'ModAuthor');
else
log(" Menu["$i$"]:"$MenuStack[i].Name,'ModAuthor');
}
}
// Close the current menu manually before we clean up the stack.
if ( bForced )
MainNotWanted = True;
if ( MenuStack.Length > 0 )
SaveRestorePages();
if( MenuStack.Length >= 0 )
CloseMenu(bCancel);
MainNotWanted = False;
for ( i = MenuStack.Length - 1;i >= 0; i-- )
PopMenu(i, MenuStack[i], bCancel);
//log("GUIController::CloseAll - After Menu closing "@GameResolution);
if (GameResolution != "")
{
Console(Master.Console).DelayedConsoleCommand("SETRES"@GameResolution);
GameResolution="";
}
ActivePage=None;
SetControllerStatus(false);
}
function int Count()
{
return MenuStack.Length;
}
function SaveRestorePages()
{
local int i;
local string Param1, Param2, MenuClass;
// First, clear any current entries
RestoreMenus.Remove( 0, RestoreMenus.Length );
for ( i = MenuStack.Length - 1; i >= 0; i-- )
{
if ( MenuStack[i] != None )
{
if ( MenuStack[i].bRestorable )
{
MenuClass = string(MenuStack[i].Class);
Param1 = "";
Param2 = "";
if ( MenuStack[i].GetRestoreParams(Param1, Param2) )
{
RestoreMenus.Insert(0, 1);
RestoreMenus[0].MenuClassName = MenuClass;
RestoreMenus[0].Param1 = Param1;
RestoreMenus[0].Param2 = Param2;
}
}
}
}
}
function PerformRestore()
{
local int i, idx;
if ( bModAuthor )
log("Restoring previously open menus ("$RestoreMenus.Length$" menus to restore)",'ModAuthor');
for ( i = 0; i < RestoreMenus.Length; i++ )
{
idx = FindMenuIndexByName(RestoreMenus[i].MenuClassName);
if ( idx == -1 )
OpenMenu(RestoreMenus[i].MenuClassName, RestoreMenus[i].Param1, RestoreMenus[i].Param2);
}
RestoreMenus.Remove(0, RestoreMenus.Length);
}
function SetControllerStatus(bool On)
{
local bool bWasActive;
if ( bActive && !On && ViewportOwner != None && ViewportOwner.Actor != None )
ViewportOwner.Actor.UnpressButtons();
bWasActive = bActive;
bActive = On;
bVisible = On;
bRequiresTick=On;
if ( bActive && !bWasActive && ViewportOwner != None && ViewportOwner.Actor != None )
ViewportOwner.Actor.UnpressButtons();
ViewportOwner.bShowWindowsMouse = On && ViewportOwner.bWindowsMouseAvailable;
if (On)
bIgnoreUntilPress = true;
else
{
ResetDesigner();
ViewportOwner.Actor.ConsoleCommand("toggleime 0");
}
PurgeComponentClasses();
}
event ChangeFocus(GUIComponent Who)
{
if ( Who != None )
Who.SetFocus(None);
return;
}
function ResetFocus()
{
MoveControl = None;
if (ActiveControl!=None)
{
ActiveControl.MenuStateChange(MSAT_Blurry);
ActiveControl = None;
}
if ( FocusedControl != None )
FocusedControl.LoseFocus(None);
RepeatKey=0;
RepeatTime=0;
}
event MoveFocused(GUIComponent C, int bmLeft, int bmTop, int bmWidth, int bmHeight, float ClipX, float ClipY, float Val)
{
if ( C.bScaleToParent && C.MenuOwner != None )
{
ClipX = C.MenuOwner.ActualWidth();
ClipY = C.MenuOwner.ActualHeight();
}
if (bmLeft!=0)
{
if ( C.WinLeft < 2.0 && C.WinLeft > -2.0 && !C.bNeverScale )
C.WinLeft += (Val/ClipX) * bmLeft;
else
C.WinLeft += Val*bmLeft;
}
if (bmTop!=0)
{
if ( C.WinTop < 2.0 && C.WinTop > -2.0 && !C.bNeverScale )
C.WinTop += (Val/ClipY) * bmTop;
else
C.WinTop += Val*bmTop;
}
if (bmWidth!=0)
{
if (C.WinWidth < 2.0 && C.WinWidth > -2.0 && !C.bNeverScale)
C.WinWidth += (Val/ClipX) * bmWidth;
else
C.WinWidth += Val*bmWidth;
}
if (bmHeight!=0)
{
if (C.WinHeight <= 2.0)
C.WinHeight += (Val/ClipX) * bmHeight;
else
C.WinHeight += Val*bmHeight;
}
}
function bool HasMouseMoved( optional float ErrorMargin )
{
return Abs(MouseX - LastMouseX) > Abs(ErrorMargin) || Abs(MouseY - LastMouseY) > Abs(ErrorMargin);
}
event bool CanShowHints()
{
if ( bNoToolTips )
return false;
if ( ActivePage != None && ActivePage.bCaptureMouse )
return false;
if ( DropSource != None || DropTarget != None )
return false;
if ( !bDesignModeToolTips && bDesignMode && bHighlightCurrent )
return false;
return true;
}
event bool NeedsMenuResolution()
{
local int i;
for ( i = MenuStack.Length - 1; i >= 0; i-- )
{
if ( MenuStack[i] != None && MenuStack[i].bRequire640x480 )
return true;
if ( !MenuStack[i].bRenderWorld )
break;
}
return false;
}
event SetRequiredGameResolution(string GameRes)
{
GameResolution = GameRes;
}
event NotifyLevelChange()
{
local int i;
if ( bModAuthor )
log(Class@"NotifyLevelChange()",'ModAuthor');
ResetDesigner();
if ( bActive && ViewportOwner.Actor != None && ViewportOwner.Actor.Level != None && ViewportOwner.Actor.Level.IsPendingConnection() )
SaveRestorePages();
for (i = MenuStack.Length - 1; i >= 0 && MenuStack.Length != 0; i--)
{
// Menus might start closing other menus during NotifyLevelChange, so always check for None
if (MenuStack[i] != None)
{
// If menu is in persistent stack, skip notification here.
if ( MenuStack[i].bPersistent )
continue;
if ( MenuStack[i].NotifyLevelChange() )
RemoveMenu(MenuStack[i], True);
}
}
for (i = PersistentStack.Length - 1; i >= 0; i--)
{
if (PersistentStack[i] != None && PersistentStack[i].NotifyLevelChange())
{
if ( PersistentStack[i].IsOpen() )
RemoveMenu( PersistentStack[i], True);
else PersistentStack[i].Free();
if ( !PersistentStack[i].bPersistent )
PersistentStack.Remove(i,1);
}
}
PurgeObjectReferences();
VerifyStack();
if ( MenuStack.Length > 0 )
RestoreMenus.Remove(0,RestoreMenus.Length);
}
// =====================================================================================================================
// =====================================================================================================================
// Menu-stack maintenance & cleanup
// =====================================================================================================================
// =====================================================================================================================
function CloseOverlays()
{
MouseOver = None;
ContextMenu = None;
}
function VerifyStack()
{
local int i;
for ( i = 0; i < MenuStack.Length; i++ )
{
if ( MenuStack[i] == None || (MenuStack[i].Controller == None && bCurMenuInitialized) )
{
MenuStack.Remove(i--, 1);
continue;
}
if ( i > 0 )
MenuStack[i].ParentPage = MenuStack[i-1];
}
ConsolidateMenus();
}
function PurgeObjectReferences()
{
local class<GUIStyles> OriginalStyle;
local int i;
// Remove any temporary (i.e. mod-defined) styles
for ( i = 0; i < STYLE_NUM; i++ )
{
if ( StyleStack[i] == None )
{
OriginalStyle = class<GUIStyles>(DynamicLoadObject(DefaultStyleNames[i],class'Class'));
if ( !RegisterStyle(OriginalStyle) )
{
log("Could not restore default style "$i$" ("$DefaultStyleNames[i]$")");
continue;
}
}
if ( StyleStack[i].bTemporary )
{
OriginalStyle = class<GUIStyles>(DynamicLoadObject(DefaultStyleNames[i],class'Class'));
if ( !RegisterStyle(OriginalStyle) )
{
log("Could not restore default style "$i$" ("$DefaultStyleNames[i]$")");
StyleStack[i] = None;
}
}
}
if (StyleStack.Length > STYLE_NUM)
StyleStack.Remove(STYLE_NUM, StyleStack.Length - STYLE_NUM);
if (FontStack.Length > FONT_NUM)
FontStack.Remove(FONT_NUM, FontStack.Length - FONT_NUM);
if (MouseCursors.Length > CURSOR_NUM)
MouseCursors.Remove(CURSOR_NUM, MouseCursors.Length - CURSOR_NUM);
PurgeComponentClasses();
}
// Should be overridden
function PurgeComponentClasses();
// This is used to remove any duplicate clases from the menu stack (keeping the topmost one)
function ConsolidateMenus()
{
local int i, j;
for ( i = MenuStack.Length - 1; i >= 0; i-- )
{
for ( j = 0; j < i; j++ )
{
if ( MenuStack[i].Class == MenuStack[j].Class )
{
MenuStack[j+1].ParentPage = MenuStack[j].ParentPage;
MenuStack.Remove(j,1);
break;
}
}
}
}
// =====================================================================================================================
// =====================================================================================================================
// Main menu classes - should be overridden in child classes
// =====================================================================================================================
// =====================================================================================================================
static event Validate();
native static final function string GetMainMenuClass(); // Returns GameEngine.MainMenuClass
static function string GetSinglePlayerPage();
static function string GetServerBrowserPage();
static function string GetMultiplayerPage();
static function string GetInstantActionPage();
static function string GetModPage();
static function string GetSettingsPage();
static function string GetQuitPage();
// =====================================================================================================================
// =====================================================================================================================
// Keybind management
// =====================================================================================================================
// =====================================================================================================================
// SetKeyBind( "Space", "Jump" ); return true if successful
final function bool SetKeyBind( string BindKeyName, string BindKeyValue )
{
ViewportOwner.Actor.ConsoleCommand("set Input" @ BindKeyName @ BindKeyValue );
return true;
}
final function bool KeyNameFromIndex( byte iKey, out string KeyName, out string LocalizedKeyName )
{
KeyName = ViewportOwner.Actor.ConsoleCommand("KEYNAME" @ iKey);
LocalizedKeyName = ViewportOwner.Actor.ConsoleCommand("LOCALIZEDKEYNAME" @ iKey);
return KeyName != "";
}
// GetCurrentBind( "Space", CommandBoundToSpace ); return true if successful
final function bool GetCurrentBind( string BindKeyName, out string BindKeyValue )
{
if ( BindKeyName != "" )
{
BindKeyValue = ViewportOwner.Actor.ConsoleCommand("KEYBINDING" @ BindKeyName);
return BindKeyValue != "";
}
return false;
}
// GetAssignedKeys( "Jump", ArrayOfKeysThatPerformJump ); return true if successful
final function bool GetAssignedKeys( string BindAlias, out array<string> BindKeyNames, out array<string> LocalizedBindKeyNames )
{
local int i, iKey;
local string s;
BindKeyNames.Length = 0;
LocalizedBindKeyNames.Length = 0;
s = ViewportOwner.Actor.ConsoleCommand("BINDINGTOKEY" @ "\"" $ BindAlias $ "\"");
if ( s != "" )
{
Split(s, ",", BindKeyNames);
for ( i = 0; i < BindKeyNames.Length; i++ )
{
iKey = int(ViewportOwner.Actor.ConsoleCommand("KEYNUMBER" @ BindKeyNames[i]));
if ( iKey != -1 )
LocalizedBindKeyNames[i] = ViewportOwner.Actor.ConsoleCommand("LOCALIZEDKEYNAME" @ iKey);
}
}
return BindKeyNames.Length > 0;
}
// SearchBinds( "switchweapon ", ArrayofAliasesThatBeginWithSwitchWeapon ); return true if successful
final function bool SearchBinds( string BindAliasMask, out array<string> BindAliases )
{
local string s;
BindAliases.Length = 0;
s = ViewportOwner.Actor.ConsoleCommand("FINDKEYBINDS" @ "\"" $ BindAliasMask $ "\"");
if ( s != "" )
Split(s, ",", BindAliases);
return BindAliases.Length > 0;
}
final function bool KeyPressed( EInputKey iKey )
{
return KeyDown[iKey] != 0;
}
/** show a Question menu, when the page succesfully opens it returns the handle to it. by default the btnOk is used */
function GUIQuestionPage ShowQuestionDialog(string Question, optional byte Buttons, optional byte defButton)
{
local GUIQuestionPage QPage;
if (Buttons == 0) Buttons = 1;
if (OpenMenu(QuestionMenuClass))
{
QPage = GUIQuestionPage(TopPage());
QPage.SetupQuestion(Question, Buttons, defButton);
return QPage;
}
return none;
}
defaultproperties
{
Begin Object Class=UT2MenuFont Name=GUIMenuFont
Name="UT2MenuFont"
End Object
FontStack(0)=UT2MenuFont'GUIMenuFont'
Begin Object Class=UT2DefaultFont Name=GUIDefaultFont
Name="GUIDefaultFont"
End Object
FontStack(1)=UT2DefaultFont'GUIDefaultFont'
Begin Object Class=UT2LargeFont Name=GUILargeFont
Name="GUILargeFont"
End Object
FontStack(2)=UT2LargeFont'GUILargeFont'
Begin Object Class=UT2HeaderFont Name=GUIHeaderFont
Name="GUIHeaderFont"
End Object
FontStack(3)=UT2HeaderFont'GUIHeaderFont'
Begin Object Class=UT2SmallFont Name=GUISmallFont
Name="GUISmallFont"
End Object
FontStack(4)=UT2SmallFont'GUISmallFont'
Begin Object Class=UT2MidGameFont Name=GUIMidGameFont
Name="GUIMidGameFont"
End Object
FontStack(5)=UT2MidGameFont'GUIMidGameFont'
Begin Object Class=UT2SmallHeaderFont Name=GUISmallHeaderFont
Name="GUISmallHeaderFont"
End Object
FontStack(6)=UT2SmallHeaderFont'GUISmallHeaderFont'
Begin Object Class=UT2ServerListFont Name=GUIServerListFont
Name="GUIServerListFont"
End Object
FontStack(7)=UT2ServerListFont'GUIServerListFont'
Begin Object Class=UT2IRCFont Name=GUIIRCFont
Name="GUIIRCFont"
End Object
FontStack(8)=UT2IRCFont'GUIIRCFont'
FONT_NUM=9
DefaultStyleNames(0)="XInterface.STY_RoundButton"
DefaultStyleNames(1)="XInterface.STY_RoundScaledButton"
DefaultStyleNames(2)="XInterface.STY_SquareButton"
DefaultStyleNames(3)="XInterface.STY_ListBox"
DefaultStyleNames(4)="XInterface.STY_ScrollZone"
DefaultStyleNames(5)="XInterface.STY_TextButton"
DefaultStyleNames(6)="XInterface.STY_Page"
DefaultStyleNames(7)="XInterface.STY_Header"
DefaultStyleNames(8)="XInterface.STY_Footer"
DefaultStyleNames(9)="XInterface.STY_TabButton"
DefaultStyleNames(10)="XInterface.STY_CharButton"
DefaultStyleNames(11)="XInterface.STY_ArrowLeft"
DefaultStyleNames(12)="XInterface.STY_ArrowRight"
DefaultStyleNames(13)="XInterface.STY_ServerBrowserGrid"
DefaultStyleNames(14)="XInterface.STY_NoBackground"
DefaultStyleNames(15)="XInterface.STY_ServerBrowserGridHeader"
DefaultStyleNames(16)="XInterface.STY_SliderCaption"
DefaultStyleNames(17)="XInterface.STY_LadderButton"
DefaultStyleNames(18)="XInterface.STY_LadderButtonHi"
DefaultStyleNames(19)="XInterface.STY_LadderButtonActive"
DefaultStyleNames(20)="XInterface.STY_BindBox"
DefaultStyleNames(21)="XInterface.STY_SquareBar"
DefaultStyleNames(22)="XInterface.STY_MidGameButton"
DefaultStyleNames(23)="XInterface.STY_TextLabel"
DefaultStyleNames(24)="XInterface.STY_ComboListBox"
DefaultStyleNames(25)="XInterface.STY_SquareMenuButton"
DefaultStyleNames(26)="XInterface.STY_IRCText"
DefaultStyleNames(27)="XInterface.STY_IRCEntry"
DefaultStyleNames(28)="XInterface.STY_ListSelection"
DefaultStyleNames(29)="XInterface.STY_EditBox"
STYLE_NUM=30
// See Player.uc IDC_ definitions
// if _RO_
/*
// end if _RO_
MouseCursors(0)=material'InterfaceContent.MouseCursor' // Arrow
MouseCursors(1)=material'InterfaceContent.SplitterCursor' // SizeAll
MouseCursors(2)=material'InterfaceContent.SplitterCursor' // Size NE SW
MouseCursors(3)=material'InterfaceContent.SplitterCursorVert' // Size NS
MouseCursors(4)=material'InterfaceContent.SplitterCursor' // Size NW SE
MouseCursors(5)=material'InterfaceContent.SplitterCursor' // Size WE
MouseCursors(6)=material'InterfaceContent.MouseCursor' // Wait
// if _RO_
*/
// end if _RO_
CURSOR_NUM=7
MouseCursorOffset(0)=(X=0,Y=0,Z=0)
MouseCursorOffset(1)=(X=0.5,Y=0.5,Z=0)
MouseCursorOffset(2)=(X=0.5,Y=0.5,Z=0)
MouseCursorOffset(3)=(X=0.5,Y=0.5,Z=0)
MouseCursorOffset(4)=(X=0.5,Y=0.5,Z=0)
MouseCursorOffset(5)=(X=0.5,Y=0.5,Z=0)
MouseCursorOffset(6)=(X=0,Y=0,Z=0)
ButtonRepeatDelay=0.25
CursorStep=1
FastCursorStep=1
// Design Mode stuff
// if _RO
WhiteBorder=Texture'InterfaceArt_tex.Menu.WhiteBorder'
// else
// WhiteBorder=Material'InterfaceContent.Menu.WhiteBorder'
// end if _RO_
DblClickWindow=0.5
// Replaceme
MouseOverSound=sound'KF_MenuSnd.msfxMouseOver'//msfxMouseOver
ClickSound=sound'KF_MenuSnd.msfxMouseClick'
EditSound=sound'KF_MenuSnd.msfxEdit'
UpSound=sound'KF_MenuSnd.msfxUp'
DownSound=sound'KF_MenuSnd.msfxDown'
MenuMouseSens=1.0
bHighlightCurrent=true
bModulateStackedMenus=true
bCurMenuInitialized=True
RequestDataMenu="GUI2K4.UT2K4GetDataMenu"
ArrayPropertyMenu="GUI2K4.GUIArrayPropPage"
DynArrayPropertyMenu="GUI2K4.GUIDynArrayPage"
QuestionMenuClass="XInterface.GUIQuestionPage"
FilterMenu="GUI2K4.UT2K4_FilterListPage"
MapVotingMenu="xVoting.MapVotingPage"
KickVotingMenu="xVoting.KickVotingPage"
MatchSetupMenu="xVoting.MatchConfigPage"
DesignerMenu="GUIDesigner.PropertyManager"
EditFavoriteMenu="GUI2K4.EditFavoritePage"
NetworkMsgMenu="GUI2K4.UT2K4NetworkStatusMsg"
// items in parenthesis means 'while held'
// items in brackets only required if NOT in interactive mode
DesignModeHints(0)=(Key=" Key",Description=" Description")
DesignModeHints(1)=(Key=" (F1)",Description=" View this help screen")
DesignModeHints(2)=(Key=" Ctrl + Alt + D",Description=" Toggles design mode")
DesignModeHints(3)=(Key=" Ctrl + Alt + E",Description=" Toggles property editor mode")
DesignModeHints(4)=(Key=" [Ctrl +] H",Description=" Toggles active/focused info")
DesignModeHints(5)=(Key=" [Ctrl +] I",Description=" Toggle interactive mode")
DesignModeHints(6)=(Key=" [Ctrl +] P",Description=" Toggles full MenuOwner chains for active/focused")
DesignModeHints(7)=(Key=" [Ctrl +] C",Description=" Copy MoveControl position to clipboard")
DesignModeHints(8)=(Key=" [Ctrl +] X",Description=" Export MoveControl to clipboard")
DesignModeHints(9)=(Key=" [Ctrl +] U",Description=" Refresh the property window in the designer")
DesignModeHints(10)=(Key=" [Ctrl +] Up/Down/Left/Right",Description=" Reposition MoveControl using arrow keys")
DesignModeHints(11)=(Key=" [Ctrl +] +/-",Description=" Resize MoveControl vertically")
DesignModeHints(12)=(Key=" [Ctrl +] Num+/Num-",Description=" Resize selected component horizontally")
DesignModeHints(13)=(Key=" [Ctrl +] WheelUp",Description=" Set MoveControl to MoveControl's menuowner")
DesignModeHints(14)=(Key=" [Ctrl +] WheenDown",Description=" Set MoveControl to MoveControl's focused control")
DesignModeHints(15)=(Key=" [(Ctrl + Alt) +] MouseX/Y+LMouse",Description=" Reposition MoveControl using mouse")
DesignModeHints(16)=(Key=" (Shift)",Description=" Hides all design mode indicators")
DesignModeHints(17)=(Key=" (Ctrl + Alt)",Description=" View focus chain")
DesignModeHints(18)=(Key=" [Ctrl +] Tab",Description=" Select new MoveControl")
DesignModeHints(19)=(Key=" [Ctrl +] LMouse",Description=" Select new MoveControl")
LCDLogo=texture'G15LCD.Logos.BWLogoRGB8A'
LCDTinyFont=font'Engine.Defaultfont'
LCDSmallFont=font'G15LCDFonts.LCDSmallFont'
LCDMedFont=font'G15LCDFonts.LCDMedFont'
LCDLargeFont=font'G15LCDFonts.LCDLargeFont'
MaxPingsPerSecond=0
}