aboutsummaryrefslogtreecommitdiff
path: root/Northstar.Client/mod/scripts/vscripts/ui
diff options
context:
space:
mode:
authorBobTheBob <32057864+BobTheBob9@users.noreply.github.com>2021-08-31 23:14:58 +0100
committerBobTheBob <32057864+BobTheBob9@users.noreply.github.com>2021-08-31 23:14:58 +0100
commit9a96d0bff56f1969c68bb52a2f33296095bdc67d (patch)
tree4175928e488632705692e3cccafa1a38dd854615 /Northstar.Client/mod/scripts/vscripts/ui
parent27bd240871b7c0f2f49fef137718b2e3c208e3b4 (diff)
downloadNorthstarMods-9a96d0bff56f1969c68bb52a2f33296095bdc67d.tar.gz
NorthstarMods-9a96d0bff56f1969c68bb52a2f33296095bdc67d.zip
move to new mod format
Diffstat (limited to 'Northstar.Client/mod/scripts/vscripts/ui')
-rw-r--r--Northstar.Client/mod/scripts/vscripts/ui/menu_lobby.nut1628
-rw-r--r--Northstar.Client/mod/scripts/vscripts/ui/menu_map_select.nut162
-rw-r--r--Northstar.Client/mod/scripts/vscripts/ui/menu_match_settings.nut468
-rw-r--r--Northstar.Client/mod/scripts/vscripts/ui/menu_mode_select.nut140
-rw-r--r--Northstar.Client/mod/scripts/vscripts/ui/menu_ns_connect_password.nut30
-rw-r--r--Northstar.Client/mod/scripts/vscripts/ui/menu_ns_custom_match_settings.nut140
-rw-r--r--Northstar.Client/mod/scripts/vscripts/ui/menu_ns_custom_match_settings_categories.nut68
-rw-r--r--Northstar.Client/mod/scripts/vscripts/ui/menu_ns_modmenu.nut108
-rw-r--r--Northstar.Client/mod/scripts/vscripts/ui/menu_ns_serverbrowser.nut198
-rw-r--r--Northstar.Client/mod/scripts/vscripts/ui/menu_private_match.nut804
-rw-r--r--Northstar.Client/mod/scripts/vscripts/ui/panel_mainmenu.nut844
-rw-r--r--Northstar.Client/mod/scripts/vscripts/ui/ui_utility.gnut634
12 files changed, 5224 insertions, 0 deletions
diff --git a/Northstar.Client/mod/scripts/vscripts/ui/menu_lobby.nut b/Northstar.Client/mod/scripts/vscripts/ui/menu_lobby.nut
new file mode 100644
index 00000000..3c868aab
--- /dev/null
+++ b/Northstar.Client/mod/scripts/vscripts/ui/menu_lobby.nut
@@ -0,0 +1,1628 @@
+untyped
+
+
+global function MenuLobby_Init
+
+global function InitLobbyMenu
+global function UICodeCallback_SetupPlayerListGenElements
+global function UpdateAnnouncementDialog
+global function EnableButton
+global function DisableButton
+
+global function UICodeCallback_CommunityUpdated
+global function UICodeCallback_FactionUpdated
+global function Lobby_UpdateInboxButtons
+
+global function GetTimeToRestartMatchMaking
+
+global function RefreshCreditsAvailable
+
+global function InviteFriendsIfAllowed
+global function SetPutPlayerInMatchmakingAfterDelay
+
+global function DLCStoreShouldBeMarkedAsNew
+
+global function SetNextAutoMatchmakingPlaylist
+global function GetNextAutoMatchmakingPlaylist
+
+global function OnDpadCommsButton_Activate
+
+global function GetActiveSearchingPlaylist
+
+global function Lobby_SetFDModeBasedOnSearching
+global function Lobby_IsFDMode
+global function Lobby_SetAutoFDOpen
+global function Lobby_SetFDMode
+global function Lobby_ToggleFDMode
+global function Lobby_CallsignButton3EventHandler
+global function Lobby_RefreshButtons
+
+global function OnStoreButton_Activate
+global function OnStoreBundlesButton_Activate
+global function OnStoreNewReleasesButton_Activate
+
+const string MATCHMAKING_AUDIO_CONNECTING = "menu_campaignsummary_titanunlocked"
+
+struct
+{
+ struct {
+ string playlistName = ""
+ int mapIdx = -1
+ int modeIdx = -1
+ } preCacheInfo
+
+ array searchIconElems
+ array searchTextElems
+ array matchStartCountdownElems
+ array matchStatusRuis
+
+ array creditsAvailableElems
+
+ var chatroomMenu
+ var chatroomMenu_chatroomWidget
+
+ var findGameButton
+ var inviteRoomButton
+ var inviteFriendsButton
+ var inviteFriendsToNetworkButton
+ var toggleMenuModeButton
+
+ var networksMoreButton
+
+ int inboxHeaderIndex
+ var inboxButton
+
+ int customizeHeaderIndex
+ var pilotButton
+ var titanButton
+ var boostsButton
+ var storeButton
+ var storeNewReleasesButton
+ var storeBundlesButton
+ var factionButton
+ var bannerButton
+ var patchButton
+ var statsButton
+ var networksHeader
+ var settingsHeader
+ var storeHeader
+ var browseNetworkButton
+ var faqButton
+ var dpadCommsButton
+
+ var genUpButton
+
+ array<var> lobbyButtons
+ var playHeader
+ var customizeHeader
+ var callsignHeader
+
+ float timeToRestartMatchMaking = 0
+
+ string nextAutoMatchmakingPlaylist
+
+ var callsignCard
+
+ bool putPlayerInMatchmakingAfterDelay = false
+ float matchmakingStartTime = 0.0
+ int etaTime = 0
+ int etaMaxMinutes = 15
+ string lastMixtapeMatchmakingStatus
+
+ ComboStruct &lobbyComboStruct
+
+ bool isFDMode = false
+ bool shouldAutoOpenFDMenu = false
+} file
+
+void function MenuLobby_Init()
+{
+ PrecacheHUDMaterial( $"ui/menu/lobby/player_hover" )
+ PrecacheHUDMaterial( $"ui/menu/lobby/chatroom_player" )
+ PrecacheHUDMaterial( $"ui/menu/lobby/chatroom_hover" )
+ PrecacheHUDMaterial( $"ui/menu/main_menu/motd_background" )
+ PrecacheHUDMaterial( $"ui/menu/main_menu/motd_background_happyhour" )
+
+ AddUICallback_OnLevelInit( OnLobbyLevelInit )
+}
+
+
+bool function ChatroomIsVisibleAndFocused()
+{
+ return Hud_IsVisible( file.chatroomMenu ) && Hud_IsFocused( file.chatroomMenu_chatroomWidget );
+}
+
+bool function ChatroomIsVisibleAndNotFocused()
+{
+ return Hud_IsVisible( file.chatroomMenu ) && !Hud_IsFocused( file.chatroomMenu_chatroomWidget );
+}
+
+void function Lobby_UpdateInboxButtons()
+{
+ var menu = GetMenu( "LobbyMenu" )
+ if ( GetUIPlayer() == null || !IsPersistenceAvailable() )
+ return
+
+ bool hasNewMail = (Inbox_HasUnreadMessages() && Inbox_GetTotalMessageCount() > 0) || PlayerRandomUnlock_GetTotal( GetUIPlayer() ) > 0
+ if ( hasNewMail )
+ {
+ int messageCount = Inbox_GetTotalMessageCount()
+ int lootCount = PlayerRandomUnlock_GetTotal( GetUIPlayer() )
+ int totalCount = messageCount + lootCount
+
+ string countString
+ if ( totalCount >= MAX_MAIL_COUNT )
+ countString = MAX_MAIL_COUNT + "+"
+ else
+ countString = string( totalCount )
+
+ SetComboButtonHeaderTitle( menu, file.inboxHeaderIndex, Localize( "#MENU_HEADER_NETWORKS_NEW_MSGS", countString ) )
+ ComboButton_SetText( file.inboxButton, Localize( "#MENU_TITLE_INBOX_NEW_MSGS", countString ) )
+ }
+ else
+ {
+ SetComboButtonHeaderTitle( menu, file.inboxHeaderIndex, Localize( "#MENU_HEADER_NETWORKS" ) )
+ ComboButton_SetText( file.inboxButton, Localize( "#MENU_TITLE_READ" ) )
+ }
+
+ ComboButton_SetNewMail( file.inboxButton, hasNewMail )
+}
+
+void function InitLobbyMenu()
+{
+ var menu = GetMenu( "LobbyMenu" )
+
+ InitOpenInvitesMenu()
+
+ AddMenuFooterOption( menu, BUTTON_A, "#A_BUTTON_SELECT", "", null, ChatroomIsVisibleAndNotFocused )
+ AddMenuFooterOption( menu, BUTTON_B, "#B_BUTTON_BACK", "#BACK" )
+ AddMenuFooterOption( menu, BUTTON_BACK, "#BACK_BUTTON_POSTGAME_REPORT", "#POSTGAME_REPORT", OpenPostGameMenu, IsPostGameMenuValid )
+ AddMenuFooterOption( menu, BUTTON_TRIGGER_RIGHT, "#R_TRIGGER_CHAT", "", null, IsVoiceChatPushToTalk )
+
+ InitChatroom( menu )
+
+ file.chatroomMenu = Hud_GetChild( menu, "ChatRoomPanel" )
+ file.chatroomMenu_chatroomWidget = Hud_GetChild( file.chatroomMenu, "ChatRoom" )
+ file.genUpButton = Hud_GetChild( menu, "GenUpButton" )
+
+ SetupComboButtonTest( menu )
+
+ AddMenuEventHandler( menu, eUIEvent.MENU_OPEN, OnLobbyMenu_Open )
+ AddMenuEventHandler( menu, eUIEvent.MENU_CLOSE, OnLobbyMenu_Close )
+ AddMenuEventHandler( menu, eUIEvent.MENU_NAVIGATE_BACK, OnLobbyMenu_NavigateBack )
+
+ RegisterUIVarChangeCallback( "gameStartTime", GameStartTime_Changed )
+
+ RegisterUIVarChangeCallback( "showGameSummary", ShowGameSummary_Changed )
+
+ file.searchIconElems = GetElementsByClassnameForMenus( "SearchIconClass", uiGlobal.allMenus )
+ file.searchTextElems = GetElementsByClassnameForMenus( "SearchTextClass", uiGlobal.allMenus )
+ file.matchStartCountdownElems = GetElementsByClassnameForMenus( "MatchStartCountdownClass", uiGlobal.allMenus )
+ file.matchStatusRuis = GetElementsByClassnameForMenus( "MatchmakingStatusRui", uiGlobal.allMenus )
+ file.creditsAvailableElems = GetElementsByClassnameForMenus( "CreditsAvailableClass", uiGlobal.allMenus )
+
+ file.callsignCard = Hud_GetChild( menu, "CallsignCard" )
+
+ AddEventHandlerToButton( menu, "GenUpButton", UIE_CLICK, AdvanceMenuEventHandler( GetMenu( "Generation_Respawn" ) ) )
+
+ AddMenuVarChangeHandler( "focus", UpdateFooterOptions )
+ AddMenuVarChangeHandler( "isFullyConnected", UpdateFooterOptions )
+ AddMenuVarChangeHandler( "isPartyLeader", UpdateFooterOptions )
+ AddMenuVarChangeHandler( "isPrivateMatch", UpdateFooterOptions )
+ #if DURANGO_PROG
+ AddMenuVarChangeHandler( "DURANGO_canInviteFriends", UpdateFooterOptions )
+ AddMenuVarChangeHandler( "DURANGO_isJoinable", UpdateFooterOptions )
+ AddMenuVarChangeHandler( "DURANGO_isGameFullyInstalled", UpdateFooterOptions )
+ #elseif PS4_PROG
+ AddMenuVarChangeHandler( "PS4_canInviteFriends", UpdateFooterOptions )
+ #elseif PC_PROG
+ AddMenuVarChangeHandler( "ORIGIN_isEnabled", UpdateFooterOptions )
+ AddMenuVarChangeHandler( "ORIGIN_isJoinable", UpdateFooterOptions )
+ #endif
+
+ RegisterSignal( "BypassWaitBeforeRestartingMatchmaking" )
+ RegisterSignal( "PutPlayerInMatchmakingAfterDelay" )
+ RegisterSignal( "CancelRestartingMatchmaking" )
+ RegisterSignal( "LeaveParty" )
+}
+
+void function SetupComboButtonTest( var menu )
+{
+ ComboStruct comboStruct = ComboButtons_Create( menu )
+ file.lobbyComboStruct = comboStruct
+
+ int headerIndex = 0
+ int buttonIndex = 0
+ file.playHeader = AddComboButtonHeader( comboStruct, headerIndex, "#MENU_HEADER_PLAY" )
+
+ bool isModded = IsNorthstarServer()
+
+
+ // this will be the server browser
+ if ( isModded )
+ {
+ file.findGameButton = AddComboButton( comboStruct, headerIndex, buttonIndex++, "#MENU_TITLE_SERVER_BROWSER" )
+ file.lobbyButtons.append( file.findGameButton )
+ Hud_SetLocked( file.findGameButton, true )
+ Hud_AddEventHandler( file.findGameButton, UIE_CLICK, OpenServerBrowser )
+ }
+ else
+ {
+ file.findGameButton = AddComboButton( comboStruct, headerIndex, buttonIndex++, "#MENU_TITLE_FIND_GAME" )
+ file.lobbyButtons.append( file.findGameButton )
+ Hud_AddEventHandler( file.findGameButton, UIE_CLICK, BigPlayButton1_Activate )
+ }
+
+ // this is used for launching private matches now
+ if ( isModded )
+ {
+ file.inviteRoomButton = AddComboButton( comboStruct, headerIndex, buttonIndex++, "#PRIVATE_MATCH" )
+ Hud_AddEventHandler( file.inviteRoomButton, UIE_CLICK, StartPrivateMatch )
+ }
+ else
+ {
+ file.inviteRoomButton = AddComboButton( comboStruct, headerIndex, buttonIndex++, "#MENU_TITLE_INVITE_ROOM" )
+ Hud_AddEventHandler( file.inviteRoomButton, UIE_CLICK, DoRoomInviteIfAllowed )
+ }
+
+ file.inviteFriendsButton = AddComboButton( comboStruct, headerIndex, buttonIndex++, "#MENU_TITLE_INVITE_FRIENDS" )
+ Hud_AddEventHandler( file.inviteFriendsButton, UIE_CLICK, InviteFriendsIfAllowed )
+
+ if ( isModded )
+ {
+ Hud_SetEnabled( file.inviteFriendsButton, false )
+ Hud_SetVisible( file.inviteFriendsButton, false )
+ }
+
+ // file.toggleMenuModeButton = AddComboButton( comboStruct, headerIndex, buttonIndex++, "#MENU_LOBBY_SWITCH_FD" )
+ // Hud_AddEventHandler( file.toggleMenuModeButton, UIE_CLICK, ToggleLobbyMode )
+
+ headerIndex++
+ buttonIndex = 0
+ file.customizeHeader = AddComboButtonHeader( comboStruct, headerIndex, "#MENU_HEADER_LOADOUTS" )
+ file.customizeHeaderIndex = headerIndex
+ var pilotButton = AddComboButton( comboStruct, headerIndex, buttonIndex++, "#MENU_TITLE_PILOT" )
+ file.pilotButton = pilotButton
+ file.lobbyButtons.append( pilotButton )
+ Hud_AddEventHandler( pilotButton, UIE_CLICK, AdvanceMenuEventHandler( GetMenu( "EditPilotLoadoutsMenu" ) ) )
+ var titanButton = AddComboButton( comboStruct, headerIndex, buttonIndex++, "#MENU_TITLE_TITAN" )
+ file.titanButton = titanButton
+ Hud_AddEventHandler( titanButton, UIE_CLICK, AdvanceMenuEventHandler( GetMenu( "EditTitanLoadoutsMenu" ) ) )
+ file.boostsButton = AddComboButton( comboStruct, headerIndex, buttonIndex++, "#MENU_TITLE_BOOSTS" )
+ Hud_AddEventHandler( file.boostsButton, UIE_CLICK, AdvanceMenuEventHandler( GetMenu( "BurnCardMenu" ) ) )
+
+ headerIndex++
+ buttonIndex = 0
+ file.callsignHeader = AddComboButtonHeader( comboStruct, headerIndex, "#MENU_HEADER_CALLSIGN" )
+ file.bannerButton = AddComboButton( comboStruct, headerIndex, buttonIndex++, "#MENU_TITLE_BANNER" )
+ file.lobbyButtons.append( file.bannerButton )
+ Hud_AddEventHandler( file.bannerButton, UIE_CLICK, AdvanceMenuEventHandler( GetMenu( "CallsignCardSelectMenu" ) ) )
+ file.patchButton = AddComboButton( comboStruct, headerIndex, buttonIndex++, "#MENU_TITLE_PATCH" )
+ Hud_AddEventHandler( file.patchButton, UIE_CLICK, AdvanceMenuEventHandler( GetMenu( "CallsignIconSelectMenu" ) ) )
+ file.factionButton = AddComboButton( comboStruct, headerIndex, buttonIndex++, "#MENU_TITLE_FACTION" )
+ Hud_AddEventHandler( file.factionButton, UIE_CLICK, Lobby_CallsignButton3EventHandler )
+ file.statsButton = AddComboButton( comboStruct, headerIndex, buttonIndex++, "#MENU_TITLE_STATS" )
+ Hud_AddEventHandler( file.statsButton, UIE_CLICK, AdvanceMenuEventHandler( GetMenu( "ViewStatsMenu" ) ) )
+
+ file.callsignCard = Hud_GetChild( menu, "CallsignCard" )
+
+ headerIndex++
+ buttonIndex = 0
+ file.networksHeader = AddComboButtonHeader( comboStruct, headerIndex, "#MENU_HEADER_NETWORKS" )
+ file.inboxHeaderIndex = headerIndex
+ var networksInbox = AddComboButton( comboStruct, headerIndex, buttonIndex++, "#MENU_TITLE_INBOX" )
+ file.inboxButton = networksInbox
+ file.lobbyButtons.append( networksInbox )
+ Hud_AddEventHandler( networksInbox, UIE_CLICK, OnInboxButton_Activate )
+ var switchButton = AddComboButton( comboStruct, headerIndex, buttonIndex++, "#COMMUNITY_SWITCHCOMMUNITY" )
+ Hud_AddEventHandler( switchButton, UIE_CLICK, OnSwitchButton_Activate )
+ var browseButton = AddComboButton( comboStruct, headerIndex, buttonIndex++, "#COMMUNITY_BROWSE_NETWORKS" )
+ file.lobbyButtons.append( browseButton )
+ Hud_AddEventHandler( browseButton, UIE_CLICK, OnBrowseNetworksButton_Activate )
+ file.browseNetworkButton = browseButton
+ #if NETWORK_INVITE
+ file.inviteFriendsToNetworkButton = AddComboButton( comboStruct, headerIndex, buttonIndex++, "#INVITE_FRIENDS" )
+ file.lobbyButtons.append( file.inviteFriendsToNetworkButton )
+ Hud_AddEventHandler( file.inviteFriendsToNetworkButton, UIE_CLICK, OnInviteFriendsToNetworkButton_Activate )
+ #endif
+
+ headerIndex++
+ buttonIndex = 0
+ file.storeHeader = AddComboButtonHeader( comboStruct, headerIndex, "#MENU_HEADER_STORE" )
+ SetComboButtonHeaderTint( GetMenu( "LobbyMenu" ), headerIndex, true )
+ file.storeButton = AddComboButton( comboStruct, headerIndex, buttonIndex++, "#MENU_TITLE_STORE_BROWSE" )
+ Hud_AddEventHandler( file.storeButton, UIE_CLICK, OnStoreButton_Activate )
+ file.storeNewReleasesButton = AddComboButton( comboStruct, headerIndex, buttonIndex++, "#MENU_TITLE_STORE_NEW_RELEASES" )
+ Hud_AddEventHandler( file.storeNewReleasesButton, UIE_CLICK, OnStoreNewReleasesButton_Activate )
+ file.storeBundlesButton = AddComboButton( comboStruct, headerIndex, buttonIndex++, "#MENU_TITLE_STORE_BUNDLES" )
+ Hud_AddEventHandler( file.storeBundlesButton, UIE_CLICK, OnStoreBundlesButton_Activate )
+
+ headerIndex++
+ buttonIndex = 0
+ file.settingsHeader = AddComboButtonHeader( comboStruct, headerIndex, "#MENU_HEADER_SETTINGS" )
+ var controlsButton = AddComboButton( comboStruct, headerIndex, buttonIndex++, "#MENU_TITLE_CONTROLS" )
+ Hud_AddEventHandler( controlsButton, UIE_CLICK, AdvanceMenuEventHandler( GetMenu( "ControlsMenu" ) ) )
+ file.lobbyButtons.append( controlsButton )
+ #if CONSOLE_PROG
+ var avButton = AddComboButton( comboStruct, headerIndex, buttonIndex++, "#AUDIO_VIDEO" )
+ Hud_AddEventHandler( avButton, UIE_CLICK, AdvanceMenuEventHandler( GetMenu( "AudioVideoMenu" ) ) )
+ #elseif PC_PROG
+ var videoButton = AddComboButton( comboStruct, headerIndex, buttonIndex++, "#AUDIO" )
+ Hud_AddEventHandler( videoButton, UIE_CLICK, AdvanceMenuEventHandler( GetMenu( "AudioMenu" ) ) )
+ var soundButton = AddComboButton( comboStruct, headerIndex, buttonIndex++, "#VIDEO" )
+ Hud_AddEventHandler( soundButton, UIE_CLICK, AdvanceMenuEventHandler( GetMenu( "VideoMenu" ) ) )
+ #endif
+ file.faqButton = AddComboButton( comboStruct, headerIndex, buttonIndex++, "#KNB_MENU_HEADER" )
+ Hud_AddEventHandler( file.faqButton, UIE_CLICK, AdvanceMenuEventHandler( GetMenu( "KnowledgeBaseMenu" ) ) )
+
+ comboStruct.navUpButtonDisabled = true
+ comboStruct.navDownButton = file.genUpButton
+
+ ComboButtons_Finalize( comboStruct )
+}
+
+bool function MatchResultsExist()
+{
+ return true // TODO
+}
+
+void function StartPrivateMatch( var button )
+{
+ if ( Hud_IsLocked( button ) )
+ return
+
+ ClientCommand( "StartPrivateMatchSearch" )
+}
+
+void function DoRoomInviteIfAllowed( var button )
+{
+ if ( Hud_IsLocked( button ) )
+ return
+
+ if ( !DoesCurrentCommunitySupportInvites() )
+ {
+ OnBrowseNetworksButton_Activate( button )
+ return
+ }
+
+ entity player = GetUIPlayer()
+
+ if ( IsValid( player ) && Player_NextAvailableMatchmakingTime( player ) > 0 )
+ {
+ DisplayMatchmakingPenaltyDialog( player )
+ return
+ }
+
+ SendOpenInvite( true )
+ OpenSelectedPlaylistMenu()
+}
+
+void function DisplayMatchmakingPenaltyDialog( entity player )
+{
+ int minutesRemaining = int( ceil( Player_GetRemainingMatchmakingDelay( player ) / 60) )
+ if ( minutesRemaining <= 1 )
+ ServerCallback_GenericDialog( 30, 31, true )
+ else if ( minutesRemaining == 2 )
+ ServerCallback_GenericDialog( 30, 32, true )
+ else if ( minutesRemaining == 3 )
+ ServerCallback_GenericDialog( 30, 33, true )
+ else if ( minutesRemaining == 4 )
+ ServerCallback_GenericDialog( 30, 34, true )
+ else if ( minutesRemaining == 5 )
+ ServerCallback_GenericDialog( 30, 35, true )
+ else
+ ServerCallback_GenericDialog( 30, 36, true )
+}
+
+void function CreatePartyAndInviteFriends()
+{
+ if ( CanInvite() )
+ {
+ while ( !PartyHasMembers() && !AmIPartyLeader() )
+ {
+ ClientCommand( "createparty" )
+ WaitFrameOrUntilLevelLoaded()
+ }
+ InviteFriends( file.inviteFriendsButton )
+ }
+ else
+ {
+ printt( "Not inviting friends - CanInvite() returned false" );
+ }
+}
+
+void function ToggleLobbyMode( var button )
+{
+ Lobby_ToggleFDMode()
+}
+
+void function Lobby_ToggleFDMode()
+{
+ Hud_SetFocused( file.findGameButton )
+ ComboButtons_ResetColumnFocus( file.lobbyComboStruct )
+ file.isFDMode = !file.isFDMode
+ Lobby_RefreshButtons()
+}
+
+void function Lobby_CallsignButton3EventHandler( var button )
+{
+ if ( Lobby_IsFDMode() )
+ {
+ AdvanceMenu( GetMenu( "ViewStatsMenu" ) )
+ }
+ else
+ {
+ AdvanceMenu( GetMenu( "FactionChoiceMenu" ) )
+ }
+}
+
+void function InviteFriendsIfAllowed( var button )
+{
+ if ( Hud_IsLocked( button ) )
+ return
+
+ entity player = GetUIPlayer()
+ if ( IsValid( player ) && Player_NextAvailableMatchmakingTime( player ) > 0 )
+ {
+ DisplayMatchmakingPenaltyDialog( player )
+ return
+ }
+
+ #if PC_PROG
+ if ( !Origin_IsOverlayAvailable() )
+ {
+ PopUpOriginOverlayDisabledDialog()
+ return
+ }
+ #endif
+
+ thread CreatePartyAndInviteFriends()
+}
+
+bool function CanInvite()
+{
+ if ( Player_NextAvailableMatchmakingTime( GetUIPlayer() ) > 0 )
+ return false
+
+#if DURANGO_PROG
+ return ( GetMenuVarBool( "isFullyConnected" ) && GetMenuVarBool( "DURANGO_canInviteFriends" ) && GetMenuVarBool( "DURANGO_isJoinable" ) && GetMenuVarBool( "DURANGO_isGameFullyInstalled" ) )
+ #elseif PS4_PROG
+ return GetMenuVarBool( "PS4_canInviteFriends" )
+ #elseif PC_PROG
+ return ( GetMenuVarBool( "isFullyConnected" ) && GetMenuVarBool( "ORIGIN_isEnabled" ) && GetMenuVarBool( "ORIGIN_isJoinable" ) && Origin_IsOverlayAvailable() )
+ #endif
+}
+
+void function Lobby_RefreshButtons()
+{
+ bool fdMode = Lobby_IsFDMode()
+ var menu = GetMenu( "LobbyMenu" )
+
+ if ( GetTopNonDialogMenu() == GetMenu( "LobbyMenu" ) )
+ {
+ if ( fdMode )
+ UI_SetPresentationType( ePresentationType.FD_MAIN )
+ else
+ UI_SetPresentationType( ePresentationType.DEFAULT )
+ }
+
+ string buttonString = fdMode ? "#MENU_LOBBY_SWITCH_DEFAULT" : "#MENU_LOBBY_SWITCH_FD"
+ // ComboButton_SetText( file.toggleMenuModeButton, buttonString )
+
+ buttonString = fdMode ? "" : "#MENU_TITLE_BOOSTS"
+ Hud_SetEnabled( file.boostsButton, !fdMode )
+ ComboButton_SetText( file.boostsButton, buttonString )
+
+ buttonString = fdMode ? "#MENU_TITLE_STATS" : "#MENU_TITLE_FACTION"
+ ComboButton_SetText( file.factionButton, buttonString )
+
+ buttonString = fdMode ? "" : "#MENU_TITLE_STATS"
+ Hud_SetEnabled( file.statsButton, !fdMode )
+ ComboButton_SetText( file.statsButton, buttonString )
+
+ buttonString = fdMode ? "#MENU_HEADER_PLAY_FD" : "#MENU_HEADER_PLAY"
+ SetComboButtonHeaderTitle( menu, 0, buttonString )
+
+ if ( fdMode )
+ Hud_Hide( Hud_GetChild( menu, "ImgTopBar" ) )
+ else
+ Hud_Show( Hud_GetChild( menu, "ImgTopBar" ) )
+
+ ComboButtons_ResetColumnFocus( file.lobbyComboStruct )
+
+ if ( fdMode )
+ {
+ Hud_SetText( Hud_GetChild( menu, "MenuTitle" ), "" )
+ }
+ else
+ {
+ Hud_SetText( Hud_GetChild( menu, "MenuTitle" ), "#MULTIPLAYER" )
+ }
+}
+
+void function OnLobbyMenu_Open()
+{
+ Assert( IsConnected() )
+
+ // code will start loading DLC info from first party unless already done
+ InitDLCStore()
+
+ thread UpdateCachedNewItems()
+ if ( file.putPlayerInMatchmakingAfterDelay )
+ {
+ entity player = GetUIPlayer()
+ if (IsValid( player ))
+ {
+ string playlistToSearch = expect string( player.GetPersistentVar( "lastPlaylist" ) )
+ string nextAutoPlaylist = GetNextAutoMatchmakingPlaylist()
+ if ( nextAutoPlaylist.len() > 0 )
+ playlistToSearch = nextAutoPlaylist
+
+ Lobby_SetFDModeBasedOnSearching( playlistToSearch )
+ }
+ AdvanceMenu( GetMenu( "SearchMenu" ) )
+ thread PutPlayerInMatchmakingAfterDelay()
+ file.putPlayerInMatchmakingAfterDelay = false
+ }
+ else if ( uiGlobal.activeMenu == GetMenu( "LobbyMenu" ) )
+ Lobby_SetFDMode( false )
+
+ thread UpdateLobbyUI()
+ thread LobbyMenuUpdate( GetMenu( "LobbyMenu" ) )
+
+ Hud_Show( file.chatroomMenu )
+
+ Lobby_RefreshButtons()
+
+ if ( IsFullyConnected() )
+ {
+ entity player = GetUIPlayer()
+ if ( !IsValid( player ) )
+ return
+
+ while ( IsPersistenceAvailable() && (player.GetPersistentVarAsInt( "initializedVersion" ) < PERSISTENCE_INIT_VERSION) )
+ {
+ WaitFrame()
+ }
+ if ( !IsPersistenceAvailable() )
+ return
+
+ // Clear hidden boosts
+ array<ItemData> boosts = GetAllItemsOfType( eItemTypes.BURN_METER_REWARD )
+ foreach ( boost in boosts )
+ {
+ if ( boost.hidden )
+ {
+ ClearNewStatus( null, boost.ref )
+ }
+ }
+
+ UpdateCallsignElement( file.callsignCard )
+ RefreshCreditsAvailable()
+
+ bool emotesAreEnabled = EmotesEnabled()
+ // "Customize"
+ {
+ bool anyNewPilotItems = HasAnyNewPilotItems( player )
+ bool anyNewTitanItems = HasAnyNewTitanItems( player )
+ bool anyNewBoosts = HasAnyNewBoosts( player )
+ bool anyNewCommsIcons = emotesAreEnabled ? HasAnyNewDpadCommsIcons( player ) : false
+ bool anyNewCustomizeHeader = (anyNewPilotItems || anyNewTitanItems || anyNewBoosts || anyNewCommsIcons)
+
+ RuiSetBool( Hud_GetRui( file.customizeHeader ), "isNew", anyNewCustomizeHeader )
+ ComboButton_SetNew( file.pilotButton, anyNewPilotItems )
+ ComboButton_SetNew( file.titanButton, anyNewTitanItems )
+ ComboButton_SetNew( file.boostsButton, anyNewBoosts )
+ }
+
+ // "Store"
+ {
+ bool storeIsNew = DLCStoreShouldBeMarkedAsNew()
+ RuiSetBool( Hud_GetRui( file.storeHeader ), "isNew", storeIsNew )
+ ComboButton_SetNew( file.storeButton, storeIsNew )
+ }
+
+ // "Callsign"
+ {
+ bool anyNewBanners = HasAnyNewCallsignBanners( player )
+ bool anyNewPatches = HasAnyNewCallsignPatches( player )
+ bool anyNewFactions = HasAnyNewFactions( player ) && Lobby_IsFDMode()
+ bool anyNewCallsignHeader = (anyNewBanners || anyNewPatches || anyNewFactions)
+
+ RuiSetBool( Hud_GetRui( file.callsignHeader ), "isNew", anyNewCallsignHeader )
+ ComboButton_SetNew( file.bannerButton, anyNewBanners )
+ ComboButton_SetNew( file.patchButton, anyNewPatches )
+ ComboButton_SetNew( file.factionButton, anyNewFactions )
+ }
+
+ bool faqIsNew = !GetConVarBool( "menu_faq_viewed" ) || HaveNewPatchNotes() || HaveNewCommunityNotes()
+ RuiSetBool( Hud_GetRui( file.settingsHeader ), "isNew", faqIsNew )
+ ComboButton_SetNew( file.faqButton, faqIsNew )
+
+ TryUnlockSRSCallsign()
+
+ Lobby_UpdateInboxButtons()
+
+ if ( file.shouldAutoOpenFDMenu )
+ {
+ file.shouldAutoOpenFDMenu = false
+ AdvanceMenu( GetMenu( GetPlaylistMenuName() ) )
+ AdvanceMenu( GetMenu( "FDMenu" ) )
+ }
+ }
+}
+
+bool function DLCStoreShouldBeMarkedAsNew()
+{
+ if ( !IsFullyConnected() )
+ return false
+
+ if ( !IsPersistenceAvailable() )
+ return false
+
+ bool hasSeenStore = expect bool( GetPersistentVar( "hasSeenStore" ) )
+ bool result = (!hasSeenStore)
+ return result
+}
+
+void function LobbyMenuUpdate( var menu )
+{
+ EndSignal( uiGlobal.signalDummy, "CleanupInGameMenus" )
+
+ while ( GetTopNonDialogMenu() == menu )
+ {
+ bool inPendingOpenInvite = InPendingOpenInvite()
+ Hud_SetLocked( file.findGameButton, !IsPartyLeader() || inPendingOpenInvite )
+ Hud_SetLocked( file.inviteRoomButton, IsOpenInviteVisible() || GetPartySize() > 1 || inPendingOpenInvite )
+ Hud_SetLocked( file.inviteFriendsButton, inPendingOpenInvite )
+
+ bool canGenUp = false
+ if ( GetUIPlayer() )
+ canGenUp = GetPersistentVarAsInt( "xp" ) == GetMaxPlayerXP() && GetGen() < MAX_GEN
+
+ Hud_SetVisible( file.genUpButton, canGenUp )
+ Hud_SetEnabled( file.genUpButton, canGenUp )
+
+ WaitFrame()
+ }
+}
+
+void function SetNextAutoMatchmakingPlaylist( string playlistName )
+{
+ file.nextAutoMatchmakingPlaylist = playlistName
+}
+
+string function GetNextAutoMatchmakingPlaylist()
+{
+ return file.nextAutoMatchmakingPlaylist
+}
+
+void function PutPlayerInMatchmakingAfterDelay()
+{
+ Signal( uiGlobal.signalDummy, "PutPlayerInMatchmakingAfterDelay" )
+ EndSignal( uiGlobal.signalDummy, "PutPlayerInMatchmakingAfterDelay" )
+ EndSignal( uiGlobal.signalDummy, "CancelRestartingMatchmaking" )
+ EndSignal( uiGlobal.signalDummy, "LeaveParty" )
+ EndSignal( uiGlobal.signalDummy, "OnCloseLobbyMenu" )
+ EndSignal( uiGlobal.signalDummy, "CleanupInGameMenus" )
+
+ if ( AreWeMatchmaking() ) //Party member, party leader is already searching
+ return
+
+ entity player = GetUIPlayer()
+ if ( !player )
+ return
+
+ string lastPlaylist = expect string( player.GetPersistentVar( "lastPlaylist" ) )
+
+ //Bump player out of match making if they were playing coliseum and are out of tickets.
+ if ( ("coliseum" == lastPlaylist) && Player_GetColiseumTicketCount( GetLocalClientPlayer() ) <= 0 )
+ {
+ SetNextAutoMatchmakingPlaylist( "" )
+ return
+ }
+
+ // Need to know if you were a party member before the countdown starts in case you leave
+ bool wasAPartyMemberThatIsNotLeader = AmIPartyMember()
+ waitthread WaitBeforeRestartingMatchmaking()
+ // Only the leader should proceed to start matchmaking
+ if ( wasAPartyMemberThatIsNotLeader )
+ return
+
+ if ( !Console_HasPermissionToPlayMultiplayer() )
+ {
+ ClientCommand( "disconnect" )
+ return
+ }
+
+ string playlistToSearch = lastPlaylist
+ string nextAutoPlaylist = GetNextAutoMatchmakingPlaylist()
+ if ( nextAutoPlaylist.len() > 0 )
+ playlistToSearch = nextAutoPlaylist
+
+ StartMatchmakingPlaylists( playlistToSearch )
+}
+
+void function WaitBeforeRestartingMatchmaking()
+{
+ Signal( uiGlobal.signalDummy, "BypassWaitBeforeRestartingMatchmaking" )
+ EndSignal( uiGlobal.signalDummy, "BypassWaitBeforeRestartingMatchmaking" )
+
+ float timeToWait
+
+ bool isPartyMemberThatIsNotLeader = AmIPartyMember()
+ SetPutPlayerInMatchmakingAfterDelay( !isPartyMemberThatIsNotLeader )
+
+ if ( isPartyMemberThatIsNotLeader )
+ timeToWait = 99999 //HACK, JFS
+ else
+ timeToWait = GetCurrentPlaylistVarFloat( "wait_before_restarting_matchmaking_time", 30.0 )
+
+ float timeToEnd = Time() + timeToWait
+
+ UpdateTimeToRestartMatchmaking( timeToEnd )
+
+ OnThreadEnd(
+ function() : ( )
+ {
+ UpdateTimeToRestartMatchmaking( 0.0 )
+ UpdateFooterOptions()
+ }
+ )
+
+ if ( isPartyMemberThatIsNotLeader )
+ {
+ while( Time() < timeToEnd ) //Hack hack, JFS. No appropriate signals for StartMatchmaking() being called. Replace when code gives us notifications about it
+ {
+ if ( isPartyMemberThatIsNotLeader != ( AmIPartyMember() ) ) //Party Status changed. Party leader probably left?
+ break
+
+ if ( AreWeMatchmaking() ) //Need to break out if Party Leader brings us into matchmaking
+ break
+
+ WaitFrame()
+ }
+
+ }
+ else
+ {
+ wait timeToWait
+ }
+}
+
+void function OnLobbyMenu_Close()
+{
+ Signal( uiGlobal.signalDummy, "OnCloseLobbyMenu" )
+}
+
+void function OnLobbyMenu_NavigateBack()
+{
+ if ( ChatroomIsVisibleAndFocused() )
+ {
+ foreach ( button in file.lobbyButtons )
+ {
+ if ( Hud_IsVisible( button ) && Hud_IsEnabled( button ) && !Hud_IsLocked( button ) )
+ {
+ Hud_SetFocused( button )
+ return
+ }
+ }
+ }
+
+ if ( InPendingOpenInvite() )
+ LeaveOpenInvite()
+ else
+ LeaveDialog()
+}
+
+function GameStartTime_Changed()
+{
+ UpdateGameStartTimeCounter()
+}
+
+function ShowGameSummary_Changed()
+{
+ if ( level.ui.showGameSummary )
+ uiGlobal.EOGOpenInLobby = true
+}
+
+function UpdateGameStartTimeCounter()
+{
+ if ( level.ui.gameStartTime == null )
+ return
+
+ MatchmakingSetSearchText( "#STARTING_IN_LOBBY" )
+ MatchmakingSetCountdownTimer( expect float( level.ui.gameStartTime + 0.0 ), true )
+
+ HideMatchmakingStatusIcons()
+}
+
+bool function MatchmakingStatusShouldShowAsActiveSearch( string matchmakingStatus )
+{
+ if ( matchmakingStatus == "#MATCHMAKING_QUEUED" )
+ return true
+ if ( matchmakingStatus == "#MATCHMAKING_ALLOCATING_SERVER" )
+ return true
+ if ( matchmakingStatus == "#MATCHMAKING_MATCH_CONNECTING" )
+ return true
+
+ return false
+}
+
+string function GetActiveSearchingPlaylist()
+{
+ if ( !IsConnected() )
+ return ""
+ if ( !AreWeMatchmaking() )
+ return ""
+
+ string matchmakingStatus = GetMyMatchmakingStatus()
+ if ( !MatchmakingStatusShouldShowAsActiveSearch( matchmakingStatus ) )
+ return ""
+
+ string param1 = GetMyMatchmakingStatusParam( 1 )
+ return param1
+}
+
+float function CalcMatchmakingWaitTime()
+{
+ float result = ((file.matchmakingStartTime > 0.01) ? (Time() - file.matchmakingStartTime) : 0.0)
+ return result
+}
+
+float function GetMixtapeWaitTimeForPlaylist( string playlistName )
+{
+ float maxWaitTime = float( GetPlaylistVarOrUseValue( playlistName, "mixtape_timeout", "0" ) )
+ return maxWaitTime
+}
+
+void function UpdateRestartMatchmakingStatus( float time )
+{
+ if ( AmIPartyMember() )
+ {
+ MatchmakingSetSearchText( "#MATCHMAKING_WAIT_ON_PARTY_LEADER_RESTARTING_MATCHMAKING" )
+ }
+ else
+ {
+ string statusText = "#MATCHMAKING_WAIT_BEFORE_RESTARTING_MATCHMAKING"
+ string matchmakeNowText = ""
+ if ( uiGlobal.activeMenu == GetMenu( "SearchMenu" ) )
+ matchmakeNowText = Localize( "#MATCHMAKING_WAIT_MATCHMAKE_NOW" )
+ MatchmakingSetSearchText( statusText, matchmakeNowText )
+ MatchmakingSetCountdownTimer( time, false )
+ }
+}
+
+void function UpdateMatchmakingStatus()
+{
+ EndSignal( uiGlobal.signalDummy, "CleanupInGameMenus" )
+
+ OnThreadEnd(
+ function() : ()
+ {
+ printt( "Hiding all matchmaking elems due to UpdateMatchmakingStatus thread ending" )
+
+ HideMatchmakingStatusIcons()
+
+ MatchmakingSetSearchText( "" )
+ MatchmakingSetCountdownTimer( 0.0, true )
+
+ MatchmakingSetSearchVisible( false )
+ MatchmakingSetCountdownVisible( false )
+ }
+ )
+
+ MatchmakingSetSearchVisible( true )
+ MatchmakingSetCountdownVisible( true )
+
+ var lobbyMenu = GetMenu( "LobbyMenu" )
+ var searchMenu = GetMenu( "SearchMenu" )
+ var postGameMenu = GetMenu( "PostGameMenu" )
+
+ string lastActiveSearchingPlaylist
+ file.matchmakingStartTime = 0.0
+ file.etaTime = 0
+ file.etaMaxMinutes = int( GetCurrentPlaylistVarOrUseValue( "etaMaxMinutes", file.etaMaxMinutes ) )
+ file.lastMixtapeMatchmakingStatus = ""
+
+ while ( true )
+ {
+ int lobbyType = GetLobbyTypeScript()
+ string matchmakingStatus = GetMyMatchmakingStatus()
+ bool isConnectingToMatch = matchmakingStatus == "#MATCHMAKING_MATCH_CONNECTING"
+
+ {
+ string activeSearchingPlaylist = GetActiveSearchingPlaylist()
+ if ( lastActiveSearchingPlaylist != activeSearchingPlaylist )
+ {
+ if ( activeSearchingPlaylist.len() > 0 )
+ {
+ lastActiveSearchingPlaylist = activeSearchingPlaylist
+ file.matchmakingStartTime = Time()
+ }
+ else
+ {
+ lastActiveSearchingPlaylist = ""
+ file.matchmakingStartTime = 0.0
+ }
+ }
+
+ if ( isConnectingToMatch && (matchmakingStatus != file.lastMixtapeMatchmakingStatus) )
+ {
+ EmitUISound( MATCHMAKING_AUDIO_CONNECTING )
+
+ int mixtape_version = GetMixtapeMatchmakingVersion()
+ if ( IsMixtapeVersionNew() )
+ LogMixtapeHasNew( mixtape_version )
+ }
+
+ file.lastMixtapeMatchmakingStatus = matchmakingStatus
+ }
+
+ if ( level.ui.gameStartTime != null || lobbyType == eLobbyType.PRIVATE_MATCH )
+ {
+ if ( level.ui.gameStartTimerComplete )
+ {
+ MatchmakingSetSearchText( matchmakingStatus, GetMyMatchmakingStatusParam( 1 ), GetMyMatchmakingStatusParam( 2 ), GetMyMatchmakingStatusParam( 3 ), GetMyMatchmakingStatusParam( 4 ) )
+ }
+
+ if ( uiGlobal.activeMenu == searchMenu )
+ CloseActiveMenu()
+ }
+ else if ( GetTimeToRestartMatchMaking() > 0 )
+ {
+ UpdateRestartMatchmakingStatus( GetTimeToRestartMatchMaking() )
+ }
+ else if ( level.ui.gameStartTime == null )
+ {
+ MatchmakingSetCountdownTimer( 0.0, true )
+ MatchmakingSetSearchText( "" )
+ HideMatchmakingStatusIcons()
+
+ if ( !IsConnected() || !AreWeMatchmaking() )
+ {
+ if ( uiGlobal.activeMenu == searchMenu )
+ CloseActiveMenu()
+ }
+ else
+ {
+ ShowMatchmakingStatusIcons()
+
+ if ( GetActiveMenu() == lobbyMenu && !IsMenuInMenuStack( searchMenu ) )
+ {
+ CloseAllDialogs()
+ AdvanceMenu( searchMenu )
+ }
+
+ var statusEl = Hud_GetChild( searchMenu, "MatchmakingStatusBig" )
+ if ( matchmakingStatus == "#MATCH_NOTHING" )
+ {
+ Hud_Hide( statusEl )
+ }
+ else if ( MatchmakingStatusShouldShowAsActiveSearch( matchmakingStatus ) )
+ {
+ string playlistName = GetMyMatchmakingStatusParam( 1 )
+ int etaSeconds = int( GetMyMatchmakingStatusParam( 2 ) )
+ int mapIdx = int( GetMyMatchmakingStatusParam( 3 ) )
+ int modeIdx = int( GetMyMatchmakingStatusParam( 4 ) )
+ string playlistList = GetMyMatchmakingStatusParam( 5 )
+
+ {
+ string statusText = Localize( "#MATCHMAKING_PLAYLISTS" )
+ RuiSetString( Hud_GetRui( statusEl ), "statusText", statusText )
+ for ( int idx = 1; idx <= 5; ++idx )
+ RuiSetString( Hud_GetRui( statusEl ), ("bulletPointText" + idx), "" )
+
+ const int MAX_SHOWN_PLAYLISTS = 9
+ array< string > searchingPlaylists = split( playlistList, "," )
+ int searchingCount = minint( searchingPlaylists.len(), MAX_SHOWN_PLAYLISTS )
+ RuiSetInt( Hud_GetRui( statusEl ), "playlistCount", searchingCount )
+ for( int idx = 0; idx < searchingCount; ++idx )
+ {
+ asset playlistThumbnail = GetPlaylistThumbnailImage( searchingPlaylists[idx] )
+ RuiSetImage( Hud_GetRui( statusEl ), format( "playlistIcon%d", idx ), playlistThumbnail )
+ }
+ }
+
+ Hud_Show( statusEl )
+
+ if ( mapIdx > -1 && modeIdx > -1 )
+ {
+ if ( file.preCacheInfo.playlistName != playlistName || file.preCacheInfo.mapIdx != mapIdx || file.preCacheInfo.modeIdx != modeIdx )
+ {
+ file.preCacheInfo.playlistName = playlistName
+ file.preCacheInfo.mapIdx = mapIdx
+ file.preCacheInfo.modeIdx = modeIdx
+ }
+ }
+
+ string etaStr = ""
+ if ( !etaSeconds && !isConnectingToMatch )
+ {
+ matchmakingStatus = "#MATCHMAKING_SEARCHING_FOR_MATCH"
+ }
+ else
+ {
+ int now = int( Time() )
+ int etaTime = now + etaSeconds
+ if ( !file.etaTime || etaTime < file.etaTime )
+ file.etaTime = etaTime
+
+ int etaSeconds = file.etaTime - now
+ if ( etaSeconds <= 0 )
+ file.etaTime = etaTime
+
+ etaSeconds = file.etaTime - now
+ if ( etaSeconds <= 90 )
+ {
+ etaStr = Localize( "#MATCHMAKING_ETA_SECONDS", etaSeconds )
+ }
+ else
+ {
+ int etaMinutes = int( ceil( etaSeconds / 60.0 ) )
+ if ( etaMinutes < file.etaMaxMinutes )
+ etaStr = Localize( "#MATCHMAKING_ETA_MINUTES", etaMinutes )
+ else
+ etaStr = Localize( "#MATCHMAKING_ETA_UNKNOWN", etaMinutes )
+ }
+ }
+
+ MatchmakingSetSearchText( matchmakingStatus, etaStr )
+ }
+ else
+ {
+ Hud_Show( statusEl )
+ RuiSetString( Hud_GetRui( statusEl ), "statusText", "" )
+ RuiSetInt( Hud_GetRui( statusEl ), "playlistCount", 0 )
+ }
+ }
+ }
+
+ WaitFrameOrUntilLevelLoaded()
+ }
+}
+
+void function UpdateAnnouncementDialog()
+{
+ while ( IsLobby() && IsFullyConnected() )
+ {
+ // Only safe on these menus. Not safe if these variables are true because they indicate the search menu or postgame menu are going to be opened.
+ if ( ( uiGlobal.activeMenu == GetMenu( "LobbyMenu" ) || uiGlobal.activeMenu == GetMenu( "PrivateLobbyMenu" ) ) && !file.putPlayerInMatchmakingAfterDelay && !uiGlobal.EOGOpenInLobby )
+ {
+ entity player = GetUIPlayer()
+
+ // Only initialize here, CloseAnnouncementDialog() handles setting it when closing
+ if ( uiGlobal.announcementVersionSeen == -1 )
+ uiGlobal.announcementVersionSeen = player.GetPersistentVarAsInt( "announcementVersionSeen" )
+
+ int announcementVersion = GetConVarInt( "announcementVersion" )
+ if ( announcementVersion > uiGlobal.announcementVersionSeen )
+ {
+ OpenAnnouncementDialog()
+ }
+ else if ( uiGlobal.activeMenu != "AnnouncementDialog" && ShouldShowEmotesAnnouncement( player ) )
+ {
+ OpenCommsIntroDialog()
+ }
+ }
+
+ WaitFrame()
+ }
+}
+
+bool function CurrentMenuIsPVEMenu()
+{
+ var topMenu = GetTopNonDialogMenu()
+ if ( topMenu == null )
+ return false
+
+ return (uiGlobal.menuData[topMenu].isPVEMenu)
+}
+
+void function RefreshCreditsAvailable( int creditsOverride = -1 )
+{
+ entity player = GetUIPlayer()
+ if ( !IsValid( player ) )
+ return
+ if ( !IsPersistenceAvailable() )
+ return
+
+ int credits = creditsOverride >= 0 ? creditsOverride : GetAvailableCredits( GetLocalClientPlayer() )
+ string pveTitle = ""
+ int pveCredits = 0
+ bool isPVE = CurrentMenuIsPVEMenu()
+ if ( isPVE )
+ {
+ TitanLoadoutDef loadout = GetCachedTitanLoadout( uiGlobal.editingLoadoutIndex )
+ pveCredits = GetAvailableFDUnlockPoints( player, loadout.titanClass )
+ pveTitle = GetTitanLoadoutName( loadout )
+ }
+
+ foreach ( elem in file.creditsAvailableElems )
+ {
+ SetUIPlayerCreditsInfo( elem, credits, GetLocalClientPlayer().GetXP(), GetGen(), GetLevel(), GetNextLevel( GetLocalClientPlayer() ), isPVE, pveCredits, pveTitle )
+ }
+}
+
+void function SetUIPlayerCreditsInfo( var infoElement, int credits, int xp, int gen, int level, int nextLevel, bool isPVE, int pveCredits, string pveTitle )
+{
+ var rui = Hud_GetRui( infoElement )
+ RuiSetInt( rui, "credits", credits )
+ RuiSetString( rui, "nameText", GetPlayerName() )
+
+ RuiSetBool( rui, "isPVE", isPVE )
+ if ( isPVE )
+ {
+ RuiSetInt( rui, "pveCredits", pveCredits )
+ RuiSetString( rui, "pveTitle", pveTitle )
+ }
+
+ if ( xp == GetMaxPlayerXP() && gen < MAX_GEN )
+ {
+ RuiSetString( rui, "levelText", PlayerXPDisplayGenAndLevel( gen, level ) )
+ RuiSetString( rui, "nextLevelText", Localize( "#REGEN_AVAILABLE" ) )
+ RuiSetInt( rui, "numLevelPips", GetXPPipsForLevel( level - 1 ) )
+ RuiSetInt( rui, "filledLevelPips", GetXPPipsForLevel( level - 1 ) )
+ }
+ else if ( xp == GetMaxPlayerXP() && gen == MAX_GEN )
+ {
+ RuiSetString( rui, "levelText", PlayerXPDisplayGenAndLevel( gen, level ) )
+ RuiSetString( rui, "nextLevelText", Localize( "#MAX_GEN" ) )
+ RuiSetInt( rui, "numLevelPips", GetXPPipsForLevel( level - 1 ) )
+ RuiSetInt( rui, "filledLevelPips", GetXPPipsForLevel( level - 1 ) )
+ }
+ else
+ {
+ RuiSetString( rui, "levelText", PlayerXPDisplayGenAndLevel( gen, level ) )
+ RuiSetString( rui, "nextLevelText", PlayerXPDisplayGenAndLevel( gen, nextLevel ) )
+ RuiSetInt( rui, "numLevelPips", GetXPPipsForLevel( level ) )
+ RuiSetInt( rui, "filledLevelPips", GetXPFilledPipsForXP( xp ) )
+ }
+
+ CallsignIcon callsignIcon = PlayerCallsignIcon_GetActive( GetLocalClientPlayer() )
+
+ RuiSetImage( rui, "callsignIcon", callsignIcon.image )
+}
+
+void function OpenServerBrowser( var button )
+{
+ if ( Hud_IsLocked( button ) )
+ return
+
+ // nothing here yet lol
+ // look at OpenSelectedPlaylistMenu for advancing to server browser menu probably
+ AdvanceMenu( GetMenu( "ServerBrowserMenu" ) )
+}
+
+void function BigPlayButton1_Activate( var button )
+{
+ if ( Hud_IsLocked( button ) )
+ return
+
+ SendOpenInvite( false )
+ OpenSelectedPlaylistMenu()
+}
+
+function EnableButton( button )
+{
+ Hud_SetEnabled( button, true )
+ Hud_Show( button )
+}
+
+function DisableButton( button )
+{
+ Hud_SetEnabled( button, false )
+ Hud_Hide( button )
+}
+
+void function OpenSelectedPlaylistMenu()
+{
+ if ( Lobby_IsFDMode() )
+ {
+ AdvanceMenu( GetMenu( "FDMenu" ) )
+ }
+ else
+ {
+ string playlistMenuName = GetPlaylistMenuName()
+ AdvanceMenu( GetMenu( playlistMenuName ) )
+ }
+}
+
+function UpdateLobbyUI()
+{
+ if ( uiGlobal.updatingLobbyUI )
+ return
+ uiGlobal.updatingLobbyUI = true
+
+ thread UpdateLobbyType()
+ thread UpdateMatchmakingStatus()
+ thread UpdateChatroomThread()
+ //thread UpdateInviteJoinButton()
+ thread UpdateInviteFriendsToNetworkButton()
+ thread UpdatePlayerInfo()
+
+ if ( uiGlobal.menuToOpenFromPromoButton != null )
+ {
+ // Special case because this menu needs a few properties set before opening
+
+ if ( IsStoreMenu( uiGlobal.menuToOpenFromPromoButton ) )
+ {
+ string menuName = expect string( uiGlobal.menuToOpenFromPromoButton._name )
+
+ void functionref() preOpenfunc = null
+ if ( uiGlobal.menuToOpenFromPromoButton == GetMenu( "StoreMenu_WeaponSkins" ) ) // Hardcoded special case for now
+ preOpenfunc = DefaultToDLC11WeaponWarpaintBundle
+
+ OpenStoreMenu( [ menuName ], preOpenfunc )
+ }
+ else
+ {
+ AdvanceMenu( uiGlobal.menuToOpenFromPromoButton )
+ }
+
+ uiGlobal.menuToOpenFromPromoButton = null
+ }
+ else if ( uiGlobal.EOGOpenInLobby )
+ {
+ EOGOpen()
+ }
+
+ WaitSignal( uiGlobal.signalDummy, "CleanupInGameMenus" )
+ uiGlobal.updatingLobbyUI = false
+}
+
+void function UpdateInviteJoinButton()
+{
+ EndSignal( uiGlobal.signalDummy, "CleanupInGameMenus" )
+ var menu = GetMenu( "LobbyMenu" )
+
+ while ( true )
+ {
+ if ( DoesCurrentCommunitySupportInvites() )
+ ComboButton_SetText( file.inviteRoomButton, Localize( "#MENU_TITLE_INVITE_ROOM" ) )
+ else
+ ComboButton_SetText( file.inviteRoomButton, Localize( "#MENU_TITLE_JOIN_NETWORK" ) )
+
+ WaitFrame()
+ }
+}
+
+void function UpdateInviteFriendsToNetworkButton()
+{
+ EndSignal( uiGlobal.signalDummy, "CleanupInGameMenus" )
+ var menu = GetMenu( "LobbyMenu" )
+
+ while ( true )
+ {
+ bool areInvitesToNetworkNotAllowed = !DoesCurrentCommunitySupportChat()
+ if ( areInvitesToNetworkNotAllowed || ( IsCurrentCommunityInviteOnly() && !AreWeAdminInCurrentCommunity() ) )
+ DisableButton( file.inviteFriendsToNetworkButton )
+ else
+ EnableButton( file.inviteFriendsToNetworkButton )
+
+ WaitFrame()
+ }
+}
+
+function UpdateLobbyType()
+{
+ EndSignal( uiGlobal.signalDummy, "CleanupInGameMenus" )
+
+ var menu = GetMenu( "LobbyMenu" )
+ int lobbyType
+ local lastType
+ local partySize
+ local lastPartySize
+ local debugArray = [ "SOLO", "PARTY_LEADER", "PARTY_MEMBER", "MATCH", "PRIVATE_MATCH" ] // Must match enum
+
+ WaitFrameOrUntilLevelLoaded()
+
+ while ( true )
+ {
+ lobbyType = GetLobbyTypeScript()
+ partySize = GetPartySize()
+
+ if ( IsConnected() && ((lobbyType != lastType) || (partySize != lastPartySize)) )
+ {
+ if ( lastType == null )
+ printt( "Lobby lobbyType changing from:", lastType, "to:", debugArray[lobbyType] )
+ else
+ printt( "Lobby lobbyType changing from:", debugArray[lastType], "to:", debugArray[lobbyType] )
+
+ local animation = null
+
+ switch ( lobbyType )
+ {
+ case eLobbyType.SOLO:
+ animation = "SoloLobby"
+ break
+
+ case eLobbyType.PARTY_LEADER:
+ animation = "PartyLeaderLobby"
+ break
+
+ case eLobbyType.PARTY_MEMBER:
+ animation = "PartyMemberLobby"
+ break
+
+ case eLobbyType.MATCH:
+ animation = "MatchLobby"
+ break
+
+ case eLobbyType.PRIVATE_MATCH:
+ animation = "PrivateMatchLobby"
+ break
+ }
+
+ if ( animation != null )
+ {
+ menu.RunAnimationScript( animation )
+ }
+
+ // Force the animation scripts (which have zero duration) to complete before anything can cancel them.
+ ForceUpdateHUDAnimations()
+
+ lastType = lobbyType
+ lastPartySize = partySize
+ }
+
+ WaitFrameOrUntilLevelLoaded()
+ }
+}
+
+void function UICodeCallback_CommunityUpdated()
+{
+ Community_CommunityUpdated()
+ UpdateChatroomUI()
+}
+
+void function UICodeCallback_FactionUpdated()
+{
+ printt( "Faction changed! to " + GetCurrentFaction() );
+}
+
+void function UICodeCallback_SetupPlayerListGenElements( table params, int gen, int rank, bool isPlayingRanked, int pilotClassIndex )
+{
+ params.image = ""
+ params.label = ""
+ params.imageOverlay = ""
+}
+
+float function GetTimeToRestartMatchMaking()
+{
+ return file.timeToRestartMatchMaking
+}
+
+void function UpdateTimeToRestartMatchmaking( float time )//JFS: This uses UI time instead of server time, which leads to awkwardness in MatchmakingSetCountdownTimer() and the rui involved
+{
+ file.timeToRestartMatchMaking = time
+
+ if ( time > 0 )
+ {
+ UpdateRestartMatchmakingStatus( time )
+ ShowMatchmakingStatusIcons()
+ }
+ else
+ {
+ MatchmakingSetSearchText( "" )
+ MatchmakingSetCountdownTimer( 0.0, true )
+ HideMatchmakingStatusIcons()
+ }
+}
+
+void function HideMatchmakingStatusIcons()
+{
+ foreach ( element in file.searchIconElems )
+ Hud_Hide( element )
+
+ foreach ( element in file.matchStatusRuis )
+ RuiSetBool( Hud_GetRui( element ), "iconVisible", false )
+}
+
+void function ShowMatchmakingStatusIcons()
+{
+ //foreach ( element in file.searchIconElems )
+ // Hud_Show( element )
+
+ foreach ( element in file.matchStatusRuis )
+ RuiSetBool( Hud_GetRui( element ), "iconVisible", true )
+}
+
+void function MatchmakingSetSearchVisible( bool state )
+{
+ foreach ( el in file.searchTextElems )
+ {
+ //if ( state )
+ // Hud_Show( el )
+ //else
+ Hud_Hide( el )
+ }
+
+ foreach ( element in file.matchStatusRuis )
+ RuiSetBool( Hud_GetRui( element ), "statusVisible", state )
+}
+
+void function MatchmakingSetSearchText( string searchText, var param1 = "", var param2 = "", var param3 = "", var param4 = "" )
+{
+ foreach ( el in file.searchTextElems )
+ {
+ Hud_SetText( el, searchText, param1, param2, param3, param4 )
+ }
+
+ foreach ( element in file.matchStatusRuis )
+ {
+ RuiSetBool( Hud_GetRui( element ), "statusHasText", searchText != "" )
+
+ RuiSetString( Hud_GetRui( element ), "statusText", Localize( searchText, param1, param2, param3, param4 ) )
+ }
+}
+
+
+void function MatchmakingSetCountdownVisible( bool state )
+{
+ foreach ( el in file.matchStartCountdownElems )
+ {
+ //if ( state )
+ // Hud_Show( el )
+ //else
+ Hud_Hide( el )
+ }
+
+ foreach ( element in file.matchStatusRuis )
+ RuiSetBool( Hud_GetRui( element ), "timerVisible", state )
+}
+
+void function MatchmakingSetCountdownTimer( float time, bool useServerTime = true ) //JFS: useServerTime bool is awkward, comes from level.ui.gameStartTime using server time and UpdateTimeToRestartMatchmaking() uses UI time.
+{
+ foreach ( element in file.matchStatusRuis )
+ {
+ RuiSetBool( Hud_GetRui( element ), "timerHasText", time != 0.0 )
+ RuiSetGameTime( Hud_GetRui( element ), "startTime", Time() )
+ RuiSetBool( Hud_GetRui( element ), "useServerTime", useServerTime )
+ RuiSetGameTime( Hud_GetRui( element ), "timerEndTime", time )
+ }
+}
+
+void function OnLobbyLevelInit()
+{
+ UpdateCallsignElement( file.callsignCard )
+ RefreshCreditsAvailable()
+}
+
+function UpdatePlayerInfo()
+{
+ EndSignal( uiGlobal.signalDummy, "CleanupInGameMenus" )
+
+ var menu = GetMenu( "LobbyMenu" )
+
+ WaitFrameOrUntilLevelLoaded()
+
+ while ( true )
+ {
+ RefreshCreditsAvailable()
+ WaitFrame()
+ }
+}
+
+void function TryUnlockSRSCallsign()
+{
+ if ( Script_IsRunningTrialVersion() )
+ return
+
+ int numCallsignsToUnlock = 0
+
+ if ( GetTotalLionsCollected() >= GetTotalLionsInGame() )
+ numCallsignsToUnlock = 3
+ else if ( GetTotalLionsCollected() >= ACHIEVEMENT_COLLECTIBLES_2_COUNT )
+ numCallsignsToUnlock = 2
+ else if ( GetTotalLionsCollected() >= ACHIEVEMENT_COLLECTIBLES_1_COUNT )
+ numCallsignsToUnlock = 1
+ else
+ numCallsignsToUnlock = 0
+
+ if ( numCallsignsToUnlock > 0 )
+ ClientCommand( "UnlockSRSCallsign " + numCallsignsToUnlock )
+}
+
+void function SetPutPlayerInMatchmakingAfterDelay( bool value )
+{
+ file.putPlayerInMatchmakingAfterDelay = value
+}
+
+void function OnStoreButton_Activate( var button )
+{
+ LaunchGamePurchaseOrDLCStore()
+}
+
+void function OnStoreNewReleasesButton_Activate( var button )
+{
+ //LaunchGamePurchaseOrDLCStore( [ "StoreMenu", "StoreMenu_NewReleases" ] )
+ LaunchGamePurchaseOrDLCStore( [ "StoreMenu", "StoreMenu_WeaponSkins" ] )
+}
+
+void function OnStoreBundlesButton_Activate( var button )
+{
+ LaunchGamePurchaseOrDLCStore( [ "StoreMenu", "StoreMenu_Sales" ] )
+}
+
+void function OnDpadCommsButton_Activate( var button )
+{
+ AdvanceMenu( GetMenu( "EditDpadCommsMenu" ) )
+}
+
+void function OpenCommsIntroDialog()
+{
+ DialogData dialogData
+ dialogData.menu = GetMenu( "AnnouncementDialog" )
+ dialogData.header = "#DPAD_COMMS_ANNOUNCEMENT_HEADER"
+ dialogData.ruiMessage.message = "#DPAD_COMMS_ANNOUNCEMENT"
+ dialogData.image = $"ui/menu/common/dialog_announcement_1"
+
+ AddDialogButton( dialogData, "#DPAD_COMMS_ANNOUNCEMENT_B1" , OpenDpadCommsMenu )
+ AddDialogButton( dialogData, "#DPAD_COMMS_ANNOUNCEMENT_B2" )
+
+ AddDialogPCBackButton( dialogData )
+ AddDialogFooter( dialogData, "#A_BUTTON_ACCEPT" )
+ AddDialogFooter( dialogData, "#B_BUTTON_BACK" )
+
+ OpenDialog( dialogData )
+
+ ClientCommand( "SetCommsIntroSeen" )
+}
+
+void function OpenDpadCommsMenu()
+{
+ OnDpadCommsButton_Activate( null )
+}
+
+bool function ShouldShowEmotesAnnouncement( entity player )
+{
+ if ( !EmotesEnabled() )
+ return false
+
+ if ( player.GetPersistentVarAsInt( "numTimesUsedComms" ) > 2 )
+ return false
+
+ if ( player.GetPersistentVar( "hasBeenIntroducedToComms" ) )
+ return false
+
+ #if !DEV
+ if ( PlayerGetRawLevel( player ) <= 2 )
+ return false
+ #endif
+
+ return true
+}
+
+void function Lobby_SetFDMode( bool mode )
+{
+ file.isFDMode = mode
+}
+
+//Function returns whether lobby is currently in "Frontier Defense" lobby mode.
+bool function Lobby_IsFDMode()
+{
+ return file.isFDMode
+}
+
+void function Lobby_SetAutoFDOpen( bool autoFD )
+{
+ Lobby_SetFDMode( autoFD )
+ file.shouldAutoOpenFDMenu = autoFD
+}
+
+void function Lobby_SetFDModeBasedOnSearching( string playlistToSearch )
+{
+ array< string > searchingPlaylists = split( playlistToSearch, "," )
+
+ bool isFDMode = false
+ int searchingCount = searchingPlaylists.len()
+ for( int idx = 0; idx < searchingCount; ++idx )
+ {
+ isFDMode = isFDMode || IsFDMode( searchingPlaylists[idx] )
+ if ( isFDMode )
+ break
+ }
+
+ Lobby_SetFDMode( isFDMode )
+} \ No newline at end of file
diff --git a/Northstar.Client/mod/scripts/vscripts/ui/menu_map_select.nut b/Northstar.Client/mod/scripts/vscripts/ui/menu_map_select.nut
new file mode 100644
index 00000000..7ed0d177
--- /dev/null
+++ b/Northstar.Client/mod/scripts/vscripts/ui/menu_map_select.nut
@@ -0,0 +1,162 @@
+untyped
+
+
+global function MenuMapSelect_Init
+
+global function InitMapsMenu
+
+struct {
+ int mapsPerPage = 21
+ int currentMapPage
+} file
+
+// note: this does have a scrolling system in vanilla, but it's honestly really weird and jank and i don't like it
+// so for parity with menu_mode_select i'm removing it in favour of a page system
+
+function MenuMapSelect_Init()
+{
+ RegisterSignal( "OnCloseMapsMenu" )
+}
+
+void function InitMapsMenu()
+{
+ var menu = GetMenu( "MapsMenu" )
+
+ AddMenuEventHandler( menu, eUIEvent.MENU_OPEN, OnOpenMapsMenu )
+ AddMenuEventHandler( menu, eUIEvent.MENU_CLOSE, OnCloseMapsMenu )
+
+ AddEventHandlerToButtonClass( menu, "MapButtonClass", UIE_GET_FOCUS, MapButton_Focused )
+ AddEventHandlerToButtonClass( menu, "MapButtonClass", UIE_LOSE_FOCUS, MapButton_LostFocus )
+ AddEventHandlerToButtonClass( menu, "MapButtonClass", UIE_CLICK, MapButton_Activate )
+
+ AddMenuFooterOption( menu, BUTTON_A, "#A_BUTTON_SELECT" )
+ AddMenuFooterOption( menu, BUTTON_B, "#B_BUTTON_BACK", "#BACK" )
+
+ AddMenuFooterOption( menu, BUTTON_SHOULDER_LEFT, "#PRIVATE_MATCH_PAGE_PREV", "#PRIVATE_MATCH_PAGE_PREV", CycleModesBack, IsNorthstarServer )
+ AddMenuFooterOption( menu, BUTTON_SHOULDER_RIGHT, "#PRIVATE_MATCH_PAGE_NEXT", "#PRIVATE_MATCH_PAGE_NEXT", CycleModesForward, IsNorthstarServer )
+}
+
+void function OnOpenMapsMenu()
+{
+ if ( IsNorthstarServer() )
+ file.mapsPerPage = 15
+ else
+ file.mapsPerPage = 21
+
+ UpdateVisibleMaps()
+}
+
+void function UpdateVisibleMaps()
+{
+ array<var> buttons = GetElementsByClassname( GetMenu( "MapsMenu" ), "MapButtonClass" )
+ array<string> mapsArray = GetPrivateMatchMaps()
+
+ foreach ( button in buttons )
+ {
+ int buttonID = int( Hud_GetScriptID( button ) )
+ int mapID = buttonID + ( file.currentMapPage * file.mapsPerPage )
+
+ if ( buttonID < file.mapsPerPage && mapID < GetPrivateMatchMaps().len() )
+ {
+ string name = mapsArray[ mapID ]
+ SetButtonRuiText( button, GetMapDisplayName( name ) )
+ Hud_SetEnabled( button, true )
+
+ if ( IsItemInEntitlementUnlock( name ) && IsValid( GetUIPlayer() ) )
+ {
+ if ( IsItemLocked( GetUIPlayer(), name ) && GetCurrentPlaylistVarInt( name + "_available" , 0 ) == 0 )
+ {
+ SetButtonRuiText( button, Localize( "#MAP_LOCKED", Localize( GetMapDisplayName( name ) ) ) )
+ }
+ }
+
+ bool mapSupportsMode = PrivateMatch_IsValidMapModeCombo( name, PrivateMatch_GetSelectedMode() )
+ Hud_SetLocked( button, !mapSupportsMode )
+
+ if ( !mapSupportsMode )
+ SetButtonRuiText( button, Localize( "#PRIVATE_MATCH_UNAVAILABLE", Localize( GetMapDisplayName( name ) ) ) )
+ }
+ else
+ {
+ SetButtonRuiText( button, "" )
+ Hud_SetEnabled( button, false )
+ }
+
+ if ( mapID == level.ui.privatematch_map )
+ {
+ printt( buttonID, mapsArray[buttonID] )
+ Hud_SetFocused( button )
+ }
+ }
+}
+
+void function OnCloseMapsMenu()
+{
+ Signal( uiGlobal.signalDummy, "OnCloseMapsMenu" )
+}
+
+void function MapButton_Focused( var button )
+{
+ int mapID = int( Hud_GetScriptID( button ) ) + ( file.currentMapPage * file.mapsPerPage )
+
+ var menu = GetMenu( "MapsMenu" )
+ var nextMapImage = Hud_GetChild( menu, "NextMapImage" )
+ var nextMapName = Hud_GetChild( menu, "NextMapName" )
+ var nextMapDesc = Hud_GetChild( menu, "NextMapDesc" )
+
+ array<string> mapsArray = GetPrivateMatchMaps()
+ string mapName = mapsArray[ mapID ]
+
+ asset mapImage = GetMapImageForMapName( mapName )
+ RuiSetImage( Hud_GetRui( nextMapImage ), "basicImage", mapImage )
+ Hud_SetText( nextMapName, GetMapDisplayName( mapName ) )
+
+ string modeName = PrivateMatch_GetSelectedMode()
+ bool mapSupportsMode = PrivateMatch_IsValidMapModeCombo( mapName, modeName )
+ if ( !mapSupportsMode )
+ Hud_SetText( nextMapDesc, Localize( "#PRIVATE_MATCH_MAP_NO_MODE_SUPPORT", Localize( GetMapDisplayName( mapName ) ), Localize( GetGameModeDisplayName( modeName ) ) ) )
+ else
+ Hud_SetText( nextMapDesc, GetMapDisplayDesc( mapName ) )
+
+}
+
+void function MapButton_LostFocus( var button )
+{
+ HandleLockedCustomMenuItem( GetMenu( "MapsMenu" ), button, [], true )
+}
+
+void function MapButton_Activate( var button )
+{
+ if ( Hud_IsLocked( button ) )
+ return
+
+ if ( !AmIPartyLeader() && GetPartySize() > 1 )
+ return
+
+ array<string> mapsArray = GetPrivateMatchMaps()
+ int mapID = int( Hud_GetScriptID( button ) )
+ string mapName = mapsArray[ mapID + ( file.currentMapPage * file.mapsPerPage ) ]
+
+ printt( mapName, mapID )
+
+ ClientCommand( "SetCustomMap " + mapName )
+ CloseActiveMenu()
+}
+
+void function CycleModesBack( var button )
+{
+ if ( file.currentMapPage == 0 )
+ return
+
+ file.currentMapPage--
+ UpdateVisibleMaps()
+}
+
+void function CycleModesForward( var button )
+{
+ if ( ( file.currentMapPage + 1 ) * file.mapsPerPage >= GetPrivateMatchMaps().len() )
+ return
+
+ file.currentMapPage++
+ UpdateVisibleMaps()
+}
diff --git a/Northstar.Client/mod/scripts/vscripts/ui/menu_match_settings.nut b/Northstar.Client/mod/scripts/vscripts/ui/menu_match_settings.nut
new file mode 100644
index 00000000..fb6cde1a
--- /dev/null
+++ b/Northstar.Client/mod/scripts/vscripts/ui/menu_match_settings.nut
@@ -0,0 +1,468 @@
+untyped
+
+global function InitMatchSettingsMenu
+
+global function UpdateMatchSettingsForGamemode
+global function MatchSettings_FormatPlaylistVarValue
+
+global const table< string, string > MatchSettings_PlaylistVarLabels = {
+ match_visibility = "#PM_MATCH_VISIBILITY",
+ roundscorelimit = "#PM_SCORE_LIMIT",
+ scorelimit = "#PM_SCORE_LIMIT",
+ roundtimelimit = "#PM_TIME_LIMIT",
+ timelimit = "#PM_TIME_LIMIT",
+ boosts_enabled = "#PM_PILOT_BOOSTS",
+ earn_meter_pilot_overdrive = "#PM_PILOT_OVERDRIVE",
+ earn_meter_pilot_multiplier = "#PM_PILOT_EARN",
+ pilot_health_multiplier = "#PM_PILOT_HEALTH",
+ respawn_delay = "#PM_PILOT_RESPAWN_DELAY",
+ riff_titan_availability = "#PM_TITAN_AVAILABILITY",
+ earn_meter_titan_multiplier = "#PM_TITAN_EARN",
+}
+
+struct PlaylistVarValueFormat
+{
+ string localizeStr
+ string formatStr
+ table< int, string > enumStrs
+}
+
+const string FORMAT_PERCENTAGE = "%d%%"
+
+struct {
+ var menu = null
+ var matchVisibilityButton = null
+ var scoreLimitButton = null
+ var scoreLimitLabel = null
+ var timeLimitButton = null
+ var timeLimitLabel = null
+ var pilotBoostsButton = null
+ var pilotOverdriveButton = null
+ var pilotEarnButton = null
+ var pilotEarnLabel = null
+ var pilotHealthButton = null
+ var pilotHealthLabel = null
+ var pilotRespawnDelayButton = null
+ var pilotRespawnDelayLabel = null
+ var titanAvailabilityButton = null
+ var titanEarnButton = null
+ var titanEarnLabel = null
+ var gameModeLabel = null
+ var matchSettingDescLabel = null
+ string modeSettingsName
+ bool isModeRoundBased
+ table< string, PlaylistVarValueFormat > playlistVarValueFormats
+} file
+
+struct
+{
+ table< string, float > timelimit = {
+ min = 1.0,
+ max = 40.0,
+ step = 1.0,
+ }
+
+ // pilot
+ table< string, float > pilot_health_multiplier = {
+ min = 0.25,
+ max = 5.0,
+ step = 0.25,
+ }
+
+ table< string, float > respawn_delay = {
+ min = 0.0,
+ max = 40.0,
+ step = 0.5,
+ }
+
+ table< string, float > earn_meter_pilot_multiplier = {
+ min = 0.25,
+ max = 5.0,
+ step = 0.25,
+ }
+
+ // titan
+ table< string, float > earn_meter_titan_multiplier = {
+ min = 0.25,
+ max = 5.0,
+ step = 0.25,
+ }
+
+ // gamemode
+ table< string, table< string, float > > scorelimit = {
+ at = {
+ min = 4000.0,
+ max = 6000.0,
+ step = 200.0,
+ }
+ ctf = {
+ min = 2.0,
+ max = 10.0,
+ step = 1.0,
+ },
+ lts = {
+ min = 2.0,
+ max = 9.0,
+ step = 1.0,
+ },
+ cp = {
+ min = 150.0,
+ max = 750.0,
+ step = 50.0,
+ },
+ mfd = {
+ min = 1.0,
+ max = 25.0,
+ step = 2.0,
+ },
+ }
+} customMatchSettings
+
+void function InitMatchSettingsMenu()
+{
+ RegisterUIVarChangeCallback( "privatematch_starting", Privatematch_starting_Changed )
+
+ var menu = GetMenu( "MatchSettingsMenu" )
+ file.menu = menu
+
+ AddMenuEventHandler( menu, eUIEvent.MENU_OPEN, OnOpenMatchSettingsMenu )
+ AddMenuEventHandler( menu, eUIEvent.MENU_NAVIGATE_BACK, OnMatchSettingsMenu_NavigateBack )
+
+ file.matchVisibilityButton = Hud_GetChild( menu, "BtnMatchVisibility" )
+ SetButtonRuiText( file.matchVisibilityButton, MatchSettings_PlaylistVarLabels["match_visibility"] )
+ PlaylistVarValueFormat matchVisibilityFormat = {
+ enumStrs = {
+ [MATCHVISIBILITY_PRIVATE] = "#MATCH_VISIBILITY_PRIVATE",
+ [MATCHVISIBILITY_LOCKED] = "#MATCH_VISIBILITY_LOCKED",
+ },
+ ...
+ }
+ file.playlistVarValueFormats["match_visibility"] <- matchVisibilityFormat
+ foreach ( int enum_, string val in matchVisibilityFormat.enumStrs )
+ Hud_DialogList_AddListItem( file.matchVisibilityButton, val, string( enum_ ) )
+
+ file.scoreLimitButton = Hud_GetChild( menu, "BtnScoreLimit" )
+ Hud_AddEventHandler( file.scoreLimitButton, UIE_CHANGE, SetScoreLimitText )
+ SetButtonRuiText( Hud_GetChild( file.scoreLimitButton, "BtnDropButton" ), MatchSettings_PlaylistVarLabels["scorelimit"] )
+ file.scoreLimitLabel = Hud_GetChild( menu, "LblScoreLimit" )
+ PlaylistVarValueFormat roundScoreLimitFormat = {
+ localizeStr = "#N_ROUND_WINS",
+ ...
+ }
+ file.playlistVarValueFormats["roundscorelimit"] <- roundScoreLimitFormat
+ PlaylistVarValueFormat scoreLimitFormat = {
+ localizeStr = "#N_POINTS",
+ ...
+ }
+ file.playlistVarValueFormats["scorelimit"] <- scoreLimitFormat
+
+ file.timeLimitButton = Hud_GetChild( menu, "BtnTimeLimit" )
+ Hud_AddEventHandler( file.timeLimitButton, UIE_CHANGE, SetTimeLimitText )
+ SetButtonRuiText( Hud_GetChild( file.timeLimitButton, "BtnDropButton" ), MatchSettings_PlaylistVarLabels["timelimit"] )
+ file.timeLimitLabel = Hud_GetChild( menu, "LblTimeLimit" )
+ PlaylistVarValueFormat roundTimeLimitFormat = {
+ localizeStr = "#N_MINUTE_ROUNDS",
+ ...
+ }
+ file.playlistVarValueFormats["roundtimelimit"] <- roundTimeLimitFormat
+ PlaylistVarValueFormat timeLimitFormat = {
+ localizeStr = "#N_MINUTES",
+ ...
+ }
+ file.playlistVarValueFormats["timelimit"] <- timeLimitFormat
+
+ // pilot
+
+ file.pilotBoostsButton = Hud_GetChild( menu, "BtnPilotBoosts" )
+ SetButtonRuiText( file.pilotBoostsButton, MatchSettings_PlaylistVarLabels["boosts_enabled"] )
+ PlaylistVarValueFormat pilotBoostsFormat = {
+ enumStrs = {
+ [eBoostAvailability.Default] = "#PILOT_BOOSTS_DEFAULT",
+ [eBoostAvailability.Disabled] = "#PILOT_BOOSTS_DISABLED",
+ },
+ ...
+ }
+ file.playlistVarValueFormats["boosts_enabled"] <- pilotBoostsFormat
+ foreach ( int enum_, string val in pilotBoostsFormat.enumStrs )
+ Hud_DialogList_AddListItem( file.pilotBoostsButton, val, string( enum_ ) )
+
+ file.pilotOverdriveButton = Hud_GetChild( menu, "BtnPilotOverdrive" )
+ SetButtonRuiText( file.pilotOverdriveButton, MatchSettings_PlaylistVarLabels["earn_meter_pilot_overdrive"] )
+ PlaylistVarValueFormat pilotOverdriveFormat = {
+ enumStrs = {
+ [ePilotOverdrive.Enabled] = "#PILOT_OVERDRIVE_ON",
+ [ePilotOverdrive.Disabled] = "#PILOT_OVERDRIVE_OFF",
+ [ePilotOverdrive.Only] = "#PILOT_OVERDRIVE_ONLY",
+ },
+ ...
+ }
+ file.playlistVarValueFormats["earn_meter_pilot_overdrive"] <- pilotOverdriveFormat
+ foreach ( int enum_, string val in pilotOverdriveFormat.enumStrs )
+ Hud_DialogList_AddListItem( file.pilotOverdriveButton, val, string( enum_ ) )
+
+ file.pilotEarnButton = Hud_GetChild( menu, "BtnPilotEarn" )
+ Hud_AddEventHandler( file.pilotEarnButton, UIE_CHANGE, SetPilotEarnText )
+ SetButtonRuiText( Hud_GetChild( file.pilotEarnButton, "BtnDropButton" ), MatchSettings_PlaylistVarLabels["earn_meter_pilot_multiplier"] )
+ file.pilotEarnLabel = Hud_GetChild( menu, "LblPilotEarn" )
+ PlaylistVarValueFormat pilotEarnFormat = {
+ formatStr = FORMAT_PERCENTAGE,
+ ...
+ }
+ file.playlistVarValueFormats["earn_meter_pilot_multiplier"] <- pilotEarnFormat
+
+ file.pilotHealthButton = Hud_GetChild( menu, "BtnPilotHealth" )
+ Hud_AddEventHandler( file.pilotHealthButton, UIE_CHANGE, SetPilotHealthText )
+ SetButtonRuiText( Hud_GetChild( file.pilotHealthButton, "BtnDropButton" ), MatchSettings_PlaylistVarLabels["pilot_health_multiplier"] )
+ file.pilotHealthLabel = Hud_GetChild( menu, "LblPilotHealth" )
+ PlaylistVarValueFormat pilotHealthFormat = {
+ formatStr = FORMAT_PERCENTAGE,
+ ...
+ }
+ file.playlistVarValueFormats["pilot_health_multiplier"] <- pilotHealthFormat
+
+ file.pilotRespawnDelayButton = Hud_GetChild( menu, "BtnPilotRespawnDelay" )
+ Hud_AddEventHandler( file.pilotRespawnDelayButton, UIE_CHANGE, SetPilotRespawnDelayText )
+ SetButtonRuiText( Hud_GetChild( file.pilotRespawnDelayButton, "BtnDropButton" ), MatchSettings_PlaylistVarLabels["respawn_delay"] )
+ file.pilotRespawnDelayLabel = Hud_GetChild( menu, "LblPilotRespawnDelay" )
+ PlaylistVarValueFormat pilotRespawnDelayFormat = {
+ localizeStr = "#N_SECONDS",
+ formatStr = "%2.1f",
+ ...
+ }
+ file.playlistVarValueFormats["respawn_delay"] <- pilotRespawnDelayFormat
+
+ // titan
+
+ file.titanAvailabilityButton = Hud_GetChild( menu, "BtnTitanAvailability" )
+ SetButtonRuiText( file.titanAvailabilityButton, MatchSettings_PlaylistVarLabels["riff_titan_availability"] )
+ PlaylistVarValueFormat titanAvailabilityFormat = {
+ enumStrs = {
+ [eTitanAvailability.Default] = "#TITAN_AVAILABILITY_DEFAULT",
+ [eTitanAvailability.Never] = "#TITAN_AVAILABILITY_NEVER",
+ },
+ ...
+ }
+ file.playlistVarValueFormats["riff_titan_availability"] <- titanAvailabilityFormat
+ foreach ( int enum_, string val in titanAvailabilityFormat.enumStrs )
+ Hud_DialogList_AddListItem( file.titanAvailabilityButton, val, string( enum_ ) )
+
+ file.titanEarnButton = Hud_GetChild( menu, "BtnTitanEarn" )
+ Hud_AddEventHandler( file.titanEarnButton, UIE_CHANGE, SetTitanEarnText )
+ SetButtonRuiText( Hud_GetChild( file.titanEarnButton, "BtnDropButton" ), MatchSettings_PlaylistVarLabels["earn_meter_titan_multiplier"] )
+ file.titanEarnLabel = Hud_GetChild( menu, "LblTitanEarn" )
+ PlaylistVarValueFormat titanEarnFormat = {
+ formatStr = FORMAT_PERCENTAGE,
+ ...
+ }
+ file.playlistVarValueFormats["earn_meter_titan_multiplier"] <- titanEarnFormat
+
+ file.gameModeLabel = Hud_GetChild( menu, "LblModeSubheaderText" )
+
+ file.matchSettingDescLabel = Hud_GetChild( menu, "LblMenuItemDescription" )
+
+ AddDescFocusHandler( file.matchVisibilityButton, "#PM_DESC_MATCH_VISIBILITY" )
+ AddDescFocusHandler( file.scoreLimitButton, "#PM_DESC_SCORE_LIMIT" )
+ AddDescFocusHandler( file.timeLimitButton, "#PM_DESC_TIME_LIMIT" )
+ AddDescFocusHandler( file.pilotBoostsButton, "#PM_DESC_PILOT_BOOSTS" )
+ AddDescFocusHandler( file.pilotOverdriveButton, "#PM_DESC_PILOT_OVERDRIVE" )
+ AddDescFocusHandler( file.pilotEarnButton, "#PM_DESC_PILOT_EARN" )
+ AddDescFocusHandler( file.pilotHealthButton, "#PM_DESC_PILOT_HEALTH" )
+ AddDescFocusHandler( file.pilotRespawnDelayButton, "#PM_DESC_PILOT_RESPAWN_DELAY" )
+ AddDescFocusHandler( file.titanAvailabilityButton, "#PM_DESC_TITAN_AVAILABILITY" )
+ AddDescFocusHandler( file.titanEarnButton, "#PM_DESC_TITAN_EARN" )
+
+ AddMenuFooterOption( menu, BUTTON_B, "#B_BUTTON_BACK", "#BACK" )
+ AddMenuFooterOption( menu, BUTTON_Y, "#Y_BUTTON_RESTORE_DEFAULTS", "#RESTORE_DEFAULTS", ResetMatchSettingsToDefaultDialog )
+}
+
+void function SetControlGamemodeAndPlaylistVar( var button, int gamemodeIdx, string playlistVar )
+{
+ Hud_SetGamemodeIdx( button, gamemodeIdx )
+ Hud_SetPlaylistVarName( button, playlistVar )
+}
+
+void function UpdateMatchSettingsForGamemode()
+{
+ file.modeSettingsName = PrivateMatch_GetSelectedMode()
+ Hud_SetText( file.gameModeLabel, GetGameModeDisplayName( file.modeSettingsName ) )
+
+ Hud_SetPlaylistVarName( file.matchVisibilityButton, "match_visibility" )
+ SetGameModeSettings()
+
+ int gamemodeIdx = expect int( level.ui.privatematch_mode )
+
+ // pilot
+
+ SetControlGamemodeAndPlaylistVar( file.pilotBoostsButton, gamemodeIdx, "boosts_enabled" )
+ SetControlGamemodeAndPlaylistVar( file.pilotOverdriveButton, gamemodeIdx, "earn_meter_pilot_overdrive" )
+ SetSliderSettings( file.pilotHealthButton, customMatchSettings.pilot_health_multiplier, gamemodeIdx, "pilot_health_multiplier" )
+ SetSliderSettings( file.pilotRespawnDelayButton, customMatchSettings.respawn_delay, gamemodeIdx, "respawn_delay" )
+ SetSliderSettings( file.pilotEarnButton, customMatchSettings.earn_meter_pilot_multiplier, gamemodeIdx, "earn_meter_pilot_multiplier" )
+
+ // titan
+
+ SetControlGamemodeAndPlaylistVar( file.titanAvailabilityButton, gamemodeIdx, "riff_titan_availability" )
+ SetSliderSettings( file.titanEarnButton, customMatchSettings.earn_meter_titan_multiplier, gamemodeIdx, "earn_meter_titan_multiplier" )
+}
+
+void function OnOpenMatchSettingsMenu()
+{
+ UpdateMatchSettingsForGamemode()
+ Hud_SetFocused( file.matchVisibilityButton )
+}
+
+void function OnMatchSettingsMenu_NavigateBack()
+{
+ CloseActiveMenu()
+}
+
+function Privatematch_starting_Changed()
+{
+ if ( GetActiveMenu() == file.menu )
+ CloseActiveMenu()
+}
+
+void function SetSliderSettings( var button, table< string, float > settings, int gamemodeIdx, string playlistVar )
+{
+ Hud_SliderControl_SetStepSize( button, settings.step )
+ Hud_SliderControl_SetMin( button, settings.min )
+ Hud_SliderControl_SetMax( button, settings.max )
+ Hud_SetGamemodeIdx( button, gamemodeIdx )
+ Hud_SetPlaylistVarName( button, playlistVar )
+ // TODO: the label should be part of the SliderControl, so there is a direct
+ // reference between the elements that make up the control. See the callbacks
+ // listening for this event and find that they have to specify their elements
+ // explicity as opposed to generically referencing releative child element(s).
+ // XXX: fire the change event to update a label with the current value
+ Hud_HandleEvent( button, UIE_CHANGE )
+}
+
+void function SetGameModeSettings()
+{
+ table< string, float > scoreSettings = {
+ min = -1.0,
+ max = -1.0,
+ step = -1.0,
+ }
+ if ( file.modeSettingsName in customMatchSettings.scorelimit )
+ scoreSettings = customMatchSettings.scorelimit[file.modeSettingsName]
+
+ int gamemodeIdx = expect int( level.ui.privatematch_mode )
+ string scoreLimitVar = "roundscorelimit"
+ string scoreLimitStr = GetCurrentPlaylistGamemodeByIndexVar( gamemodeIdx, scoreLimitVar, false )
+ string timeLimitVar = "roundtimelimit"
+ if ( scoreLimitStr.len() )
+ {
+ file.isModeRoundBased = true
+ }
+ else
+ {
+ file.isModeRoundBased = false
+ scoreLimitVar = "scorelimit"
+ scoreLimitStr = GetCurrentPlaylistGamemodeByIndexVar( gamemodeIdx, scoreLimitVar, false )
+ timeLimitVar = "timelimit"
+ }
+
+ int scoreLimit = scoreLimitStr.len() ? int( scoreLimitStr ) : 100;
+ if ( scoreSettings.min <= 0 )
+ {
+ scoreSettings.min = max( scoreLimit / 5.0, 1.0 )
+ scoreSettings.max = max( scoreLimit + (scoreLimit - scoreSettings.min), 1.0 )
+ scoreSettings.step = scoreSettings.min
+ }
+
+ SetSliderSettings( file.scoreLimitButton, scoreSettings, gamemodeIdx, scoreLimitVar )
+
+ table< string, float > timeSettings = customMatchSettings.timelimit
+ SetSliderSettings( file.timeLimitButton, timeSettings, gamemodeIdx, timeLimitVar )
+}
+
+string function MatchSettings_FormatPlaylistVarValue( string playlistVar, float value )
+{
+ if ( !(playlistVar in file.playlistVarValueFormats) )
+ return "#" + playlistVar + " unknown"
+
+ PlaylistVarValueFormat fmt = file.playlistVarValueFormats[playlistVar]
+ if ( fmt.formatStr.len() )
+ {
+ if ( fmt.formatStr == FORMAT_PERCENTAGE )
+ return format( fmt.formatStr, int( value * 100 ) )
+ else
+ return Localize( fmt.localizeStr, format( fmt.formatStr, value ) )
+ }
+
+ if ( fmt.enumStrs.len() )
+ return Localize( fmt.enumStrs[int( value )] )
+
+ return Localize( fmt.localizeStr, int( value ) )
+}
+
+void function SetScoreLimitText( var button )
+{
+ string playlistVar = file.isModeRoundBased ? "roundscorelimit" : "scorelimit"
+ float scoreLimit = Hud_SliderControl_GetCurrentValue( button )
+ string labelText = MatchSettings_FormatPlaylistVarValue( playlistVar, scoreLimit )
+ Hud_SetText( file.scoreLimitLabel, labelText )
+}
+
+void function SetTimeLimitText( var button )
+{
+ string playlistVar = file.isModeRoundBased ? "roundtimelimit" : "timelimit"
+ float timeLimit = Hud_SliderControl_GetCurrentValue( button )
+ string labelText = MatchSettings_FormatPlaylistVarValue( playlistVar, timeLimit )
+ Hud_SetText( file.timeLimitLabel, labelText )
+}
+
+void function SetPilotHealthText( var button )
+{
+ float healthMultiplier = Hud_SliderControl_GetCurrentValue( button )
+ string labelText = MatchSettings_FormatPlaylistVarValue( "pilot_health_multiplier", healthMultiplier )
+ Hud_SetText( file.pilotHealthLabel, labelText )
+}
+
+void function SetPilotRespawnDelayText( var button )
+{
+ float respawnDelay = Hud_SliderControl_GetCurrentValue( button )
+ string labelText = MatchSettings_FormatPlaylistVarValue( "respawn_delay", respawnDelay )
+ Hud_SetText( file.pilotRespawnDelayLabel, labelText )
+}
+
+void function SetPilotEarnText( var button )
+{
+ float earnMultiplier = Hud_SliderControl_GetCurrentValue( button )
+ string labelText = MatchSettings_FormatPlaylistVarValue( "earn_meter_pilot_multiplier", earnMultiplier )
+ Hud_SetText( file.pilotEarnLabel, labelText )
+}
+
+void function SetTitanEarnText( var button )
+{
+ float earnMultiplier = Hud_SliderControl_GetCurrentValue( button )
+ string labelText = MatchSettings_FormatPlaylistVarValue( "earn_meter_titan_multiplier", earnMultiplier )
+ Hud_SetText( file.titanEarnLabel, labelText )
+}
+
+void function AddDescFocusHandler( var button, string descText )
+{
+ button.s.descText <- descText
+ Hud_AddEventHandler( button, UIE_GET_FOCUS, MatchSettingsFocusUpdate )
+ if ( !Hud_HasChild( button, "BtnDropButton" ) )
+ return
+
+ var child = Hud_GetChild( button, "BtnDropButton" )
+ child.s.descText <- descText
+ Hud_AddEventHandler( child, UIE_GET_FOCUS, MatchSettingsFocusUpdate )
+
+ child = Hud_GetChild( button, "PnlDefaultMark" )
+ child.SetColor( [0,0,0,0] )
+ Hud_Hide( child )
+}
+
+void function MatchSettingsFocusUpdate( var button )
+{
+ RuiSetString( Hud_GetRui( file.matchSettingDescLabel ), "description", button.s.descText )
+}
+
+void function ResetMatchSettingsToDefaultDialog( var button )
+{
+ ClientCommand( "ResetMatchSettingsToDefault" )
+}
diff --git a/Northstar.Client/mod/scripts/vscripts/ui/menu_mode_select.nut b/Northstar.Client/mod/scripts/vscripts/ui/menu_mode_select.nut
new file mode 100644
index 00000000..a017fb42
--- /dev/null
+++ b/Northstar.Client/mod/scripts/vscripts/ui/menu_mode_select.nut
@@ -0,0 +1,140 @@
+global function InitModesMenu
+
+struct {
+ int currentModePage
+} file
+
+const int MODES_PER_PAGE = 15
+
+void function InitModesMenu()
+{
+ var menu = GetMenu( "ModesMenu" )
+
+ AddMenuEventHandler( menu, eUIEvent.MENU_OPEN, OnOpenModesMenu )
+
+ AddEventHandlerToButtonClass( menu, "ModeButton", UIE_GET_FOCUS, ModeButton_GetFocus )
+ AddEventHandlerToButtonClass( menu, "ModeButton", UIE_CLICK, ModeButton_Click )
+
+ AddMenuFooterOption( menu, BUTTON_A, "#A_BUTTON_SELECT" )
+ AddMenuFooterOption( menu, BUTTON_B, "#B_BUTTON_BACK", "#BACK" )
+
+ AddMenuFooterOption( menu, BUTTON_SHOULDER_LEFT, "#PRIVATE_MATCH_PAGE_PREV", "#PRIVATE_MATCH_PAGE_PREV", CycleModesBack, IsNorthstarServer )
+ AddMenuFooterOption( menu, BUTTON_SHOULDER_RIGHT, "#PRIVATE_MATCH_PAGE_NEXT", "#PRIVATE_MATCH_PAGE_NEXT", CycleModesForward, IsNorthstarServer )
+}
+
+void function OnOpenModesMenu()
+{
+ UpdateVisibleModes()
+
+ if ( level.ui.privatematch_mode == 0 ) // set to the first mode if there's no mode focused
+ Hud_SetFocused( GetElementsByClassname( GetMenu( "ModesMenu" ), "ModeButton" )[ 0 ] )
+}
+
+void function UpdateVisibleModes()
+{
+ // ensures that we only ever show enough buttons for the number of modes we have
+ array<var> buttons = GetElementsByClassname( GetMenu( "ModesMenu" ), "ModeButton" )
+ foreach ( var button in buttons )
+ {
+ Hud_SetEnabled( button, false )
+ Hud_SetVisible( button, false )
+ }
+
+ array<string> modesArray = GetPrivateMatchModes()
+ for ( int i = 0; i < MODES_PER_PAGE; i++ )
+ {
+ if ( i + ( file.currentModePage * MODES_PER_PAGE ) >= modesArray.len() )
+ break
+
+ int modeIndex = i + ( file.currentModePage * MODES_PER_PAGE )
+ SetButtonRuiText( buttons[ i ], GetGameModeDisplayName( modesArray[ modeIndex ] ) )
+
+ Hud_SetEnabled( buttons[ i ], true )
+ Hud_SetVisible( buttons[ i ], true )
+
+ if ( !ModeSettings_RequiresAI( modesArray[ modeIndex ] ) )
+ Hud_SetLocked( buttons[ i ], false )
+ else
+ Hud_SetLocked( buttons[ i ], true )
+
+ if ( !PrivateMatch_IsValidMapModeCombo( PrivateMatch_GetSelectedMap(), modesArray[ modeIndex ] ) && !IsNorthstarServer() )
+ {
+ Hud_SetLocked( buttons[ i ], true )
+ SetButtonRuiText( buttons[ i ], Localize( "#PRIVATE_MATCH_UNAVAILABLE", Localize( GetGameModeDisplayName( modesArray[ modeIndex ] ) ) ) )
+ }
+ }
+}
+
+void function ModeButton_GetFocus( var button )
+{
+ int modeId = int( Hud_GetScriptID( button ) ) + ( file.currentModePage * MODES_PER_PAGE )
+
+ var menu = GetMenu( "ModesMenu" )
+ var nextModeImage = Hud_GetChild( menu, "NextModeImage" )
+ var nextModeIcon = Hud_GetChild( menu, "ModeIconImage" )
+ var nextModeName = Hud_GetChild( menu, "NextModeName" )
+ var nextModeDesc = Hud_GetChild( menu, "NextModeDesc" )
+
+ array<string> modesArray = GetPrivateMatchModes()
+
+ if ( modeId > modesArray.len() )
+ return
+
+ string modeName = modesArray[modeId]
+
+ asset playlistImage = GetPlaylistImage( modeName )
+ RuiSetImage( Hud_GetRui( nextModeImage ), "basicImage", playlistImage )
+ RuiSetImage( Hud_GetRui( nextModeIcon ), "basicImage", GetPlaylistThumbnailImage( modeName ) )
+ Hud_SetText( nextModeName, GetGameModeDisplayName( modeName ) )
+
+ string mapName = PrivateMatch_GetSelectedMap()
+ bool mapSupportsMode = PrivateMatch_IsValidMapModeCombo( mapName, modeName )
+ if ( !mapSupportsMode && !IsNorthstarServer() )
+ Hud_SetText( nextModeDesc, Localize( "#PRIVATE_MATCH_MODE_NO_MAP_SUPPORT", Localize( GetGameModeDisplayName( modeName ) ), Localize( GetMapDisplayName( mapName ) ) ) )
+ else if ( IsFDMode( modeName ) ) // HACK!
+ Hud_SetText( nextModeDesc, Localize( "#FD_PLAYERS_DESC", Localize( GetGameModeDisplayHint( modeName ) ) ) )
+ else
+ Hud_SetText( nextModeDesc, GetGameModeDisplayHint( modeName ) )
+}
+
+void function ModeButton_Click( var button )
+{
+ // this never activates on custom servers, but keeping it for parity with official
+ if ( !AmIPartyLeader() && GetPartySize() > 1 )
+ return
+
+ if ( Hud_IsLocked( button ) )
+ return
+
+ int modeID = int( Hud_GetScriptID( button ) ) + ( file.currentModePage * MODES_PER_PAGE )
+
+ array<string> modesArray = GetPrivateMatchModes()
+ string modeName = modesArray[ modeID ]
+
+ // on modded servers set us to the first map for that mode automatically
+ // need this for coliseum mainly which is literally impossible to select without this
+ if ( IsNorthstarServer() && !PrivateMatch_IsValidMapModeCombo( PrivateMatch_GetSelectedMap(), modesArray[ modeID ] ) )
+ ClientCommand( "SetCustomMap " + GetPrivateMatchMapsForMode( modeName )[ 0 ] )
+
+ // set it
+ ClientCommand( "PrivateMatchSetMode " + modeName )
+ CloseActiveMenu()
+}
+
+void function CycleModesBack( var button )
+{
+ if ( file.currentModePage == 0 )
+ return
+
+ file.currentModePage--
+ UpdateVisibleModes()
+}
+
+void function CycleModesForward( var button )
+{
+ if ( ( file.currentModePage + 1 ) * MODES_PER_PAGE >= GetPrivateMatchModes().len() )
+ return
+
+ file.currentModePage++
+ UpdateVisibleModes()
+} \ No newline at end of file
diff --git a/Northstar.Client/mod/scripts/vscripts/ui/menu_ns_connect_password.nut b/Northstar.Client/mod/scripts/vscripts/ui/menu_ns_connect_password.nut
new file mode 100644
index 00000000..e1ed8991
--- /dev/null
+++ b/Northstar.Client/mod/scripts/vscripts/ui/menu_ns_connect_password.nut
@@ -0,0 +1,30 @@
+global function AddNorthstarConnectWithPasswordMenu
+
+void function AddNorthstarConnectWithPasswordMenu()
+{
+ AddMenu( "ConnectWithPasswordMenu", $"resource/ui/menus/connect_password.menu", InitConnectWithPasswordMenu, "#MENU_CONNECT" )
+}
+
+void function InitConnectWithPasswordMenu()
+{
+ AddMenuEventHandler( GetMenu( "ConnectWithPasswordMenu" ), eUIEvent.MENU_OPEN, OnConnectWithPasswordMenuOpened )
+ AddMenuFooterOption( GetMenu( "ConnectWithPasswordMenu" ), BUTTON_B, "#B_BUTTON_BACK", "#BACK" )
+
+ AddButtonEventHandler( Hud_GetChild( GetMenu( "ConnectWithPasswordMenu" ), "ConnectButton" ), UIE_CLICK, ConnectWithPassword )
+ RegisterButtonPressedCallback( KEY_ENTER, ConnectWithPassword )
+}
+
+void function OnConnectWithPasswordMenuOpened()
+{
+ UI_SetPresentationType( ePresentationType.KNOWLEDGEBASE_SUB )
+
+ Hud_SetText( Hud_GetChild( GetMenu( "ConnectWithPasswordMenu" ), "Title" ), "#MENU_TITLE_CONNECT_PASSWORD" )
+ Hud_SetText( Hud_GetChild( GetMenu( "ConnectWithPasswordMenu" ), "ConnectButton" ), "#MENU_CONNECT_MENU_CONNECT" )
+ Hud_SetText( Hud_GetChild( GetMenu( "ConnectWithPasswordMenu" ), "EnterPasswordBox" ), "" )
+}
+
+void function ConnectWithPassword( var button )
+{
+ if ( GetTopNonDialogMenu() == GetMenu( "ConnectWithPasswordMenu" ) )
+ thread ThreadedAuthAndConnectToServer( Hud_GetUTF8Text( Hud_GetChild( GetMenu( "ConnectWithPasswordMenu" ), "EnterPasswordBox" ) ) )
+} \ No newline at end of file
diff --git a/Northstar.Client/mod/scripts/vscripts/ui/menu_ns_custom_match_settings.nut b/Northstar.Client/mod/scripts/vscripts/ui/menu_ns_custom_match_settings.nut
new file mode 100644
index 00000000..d39b7774
--- /dev/null
+++ b/Northstar.Client/mod/scripts/vscripts/ui/menu_ns_custom_match_settings.nut
@@ -0,0 +1,140 @@
+global function AddNorthstarCustomMatchSettingsMenu
+global function SetNextMatchSettingsCategory
+
+const string SETTING_ITEM_TEXT = " " // this is long enough to be the same size as the textentry field
+
+struct {
+ string currentCategory
+
+ table< int, int > enumRealValues
+} file
+
+void function AddNorthstarCustomMatchSettingsMenu()
+{
+ AddMenu( "CustomMatchSettingsMenu", $"resource/ui/menus/custom_match_settings.menu", InitNorthstarCustomMatchSettingsMenu, "#MENU_MATCH_SETTINGS" )
+}
+
+void function SetNextMatchSettingsCategory( string category )
+{
+ file.currentCategory = category
+ print( "Category: " + category )
+
+ file.enumRealValues.clear()
+}
+
+void function InitNorthstarCustomMatchSettingsMenu()
+{
+ AddMenuEventHandler( GetMenu( "CustomMatchSettingsMenu" ), eUIEvent.MENU_OPEN, OnNorthstarCustomMatchSettingsMenuOpened )
+ AddMenuFooterOption( GetMenu( "CustomMatchSettingsMenu" ), BUTTON_B, "#B_BUTTON_BACK", "#BACK" )
+
+ foreach ( var button in GetElementsByClassname( GetMenu( "CustomMatchSettingsMenu" ), "MatchSettingButton" ) )
+ {
+ // it's not possible to clear dialoglists, so we have to hack together stuff that effectively recreates their functionality
+ Hud_DialogList_AddListItem( button, SETTING_ITEM_TEXT, "prev" )
+ Hud_DialogList_AddListItem( button, SETTING_ITEM_TEXT, "main" )
+ Hud_DialogList_AddListItem( button, SETTING_ITEM_TEXT, "next" )
+
+ AddButtonEventHandler( button, UIE_CHANGE, OnSettingButtonPressed )
+ }
+
+ foreach ( var textPanel in GetElementsByClassname( GetMenu( "CustomMatchSettingsMenu" ), "MatchSettingTextEntry" ) )
+ Hud_AddEventHandler( textPanel, UIE_LOSE_FOCUS, SendTextPanelChanges )
+}
+
+void function OnNorthstarCustomMatchSettingsMenuOpened()
+{
+ array<var> buttons = GetElementsByClassname( GetMenu( "CustomMatchSettingsMenu" ), "MatchSettingButton" )
+ array<var> textPanels = GetElementsByClassname( GetMenu( "CustomMatchSettingsMenu" ), "MatchSettingTextEntry" )
+
+ foreach ( var button in buttons )
+ {
+ Hud_SetEnabled( button, false )
+ Hud_SetVisible( button, false )
+ }
+
+ foreach ( var textPanel in textPanels )
+ {
+ Hud_SetEnabled( textPanel, false )
+ Hud_SetVisible( textPanel, false )
+ }
+
+ int i = 0;
+ foreach ( CustomMatchSettingContainer setting in GetPrivateMatchCustomSettingsForCategory( file.currentCategory ) )
+ {
+ Hud_SetEnabled( buttons[ i ], true )
+ Hud_SetVisible( buttons[ i ], true )
+ Hud_SetText( buttons[ i ], setting.localizedName )
+ Hud_SetDialogListSelectionValue( buttons[ i ], "main" )
+
+ Hud_SetEnabled( textPanels[ i ], true )
+ Hud_SetVisible( textPanels[ i ], true )
+
+ // manually resolve default gamemode/playlist vars since game won't do it for us if we aren't using GetCurrentPlaylistVar
+ string gamemode = PrivateMatch_GetSelectedMode()
+ if ( gamemode != "speedball" ) // hack since lf is weird
+ gamemode = GetPlaylistGamemodeByIndex( gamemode, 0 )
+
+ string gamemodeVar = GetGamemodeVarOrUseValue( PrivateMatch_GetSelectedMode(), setting.playlistVar, setting.defaultValue )
+ string playlistVar = GetPlaylistVarOrUseValue( PrivateMatch_GetSelectedMode(), setting.playlistVar, setting.defaultValue )
+
+ if ( playlistVar != gamemodeVar && playlistVar == setting.defaultValue )
+ playlistVar = gamemodeVar
+
+ if ( setting.isEnumSetting )
+ {
+ // setup internal state for enums
+ int enumIndex = int ( max( 0, setting.enumValues.find( playlistVar ) ) )
+
+ file.enumRealValues[ int( Hud_GetScriptID( textPanels[ i ] ) ) ] <- enumIndex
+ Hud_SetText( textPanels[ i ], setting.enumNames[ enumIndex ] )
+ }
+ else
+ Hud_SetText( textPanels[ i ], playlistVar )
+
+ i++
+ }
+}
+
+void function OnSettingButtonPressed( var button )
+{
+ CustomMatchSettingContainer setting = GetPrivateMatchCustomSettingsForCategory( file.currentCategory )[ int( Hud_GetScriptID( button ) ) ]
+ var textPanel = GetElementsByClassname( GetMenu( "CustomMatchSettingsMenu" ), "MatchSettingTextEntry" )[ int( Hud_GetScriptID( button ) ) ]
+
+ if ( setting.isEnumSetting )
+ {
+ string selectionVal = Hud_GetDialogListSelectionValue( button )
+ if ( selectionVal == "main" )
+ return
+
+ int enumVal = file.enumRealValues[ int( Hud_GetScriptID( button ) ) ]
+ if ( selectionVal == "next" ) // enum val += 1
+ enumVal = ( enumVal + 1 ) % setting.enumValues.len()
+ else // enum val -= 1
+ {
+ enumVal--
+ if ( enumVal == -1 )
+ enumVal = setting.enumValues.len() - 1
+ }
+
+ file.enumRealValues[ int( Hud_GetScriptID( button ) ) ] = enumVal
+ Hud_SetText( textPanel, setting.enumNames[ enumVal ] )
+
+ ClientCommand( "PrivateMatchSetPlaylistVarOverride " + setting.playlistVar + " " + setting.enumValues[ enumVal ] )
+ }
+ else
+ {
+ // this doesn't work for some reason
+ Hud_SetFocused( textPanel )
+ }
+
+ Hud_SetDialogListSelectionValue( button, "main" )
+}
+
+void function SendTextPanelChanges( var textPanel )
+{
+ CustomMatchSettingContainer setting = GetPrivateMatchCustomSettingsForCategory( file.currentCategory )[ int( Hud_GetScriptID( textPanel ) ) ]
+
+ // enums don't need to do this
+ if ( !setting.isEnumSetting )
+ ClientCommand( "PrivateMatchSetPlaylistVarOverride " + setting.playlistVar + " " + Hud_GetUTF8Text( textPanel ) )
+} \ No newline at end of file
diff --git a/Northstar.Client/mod/scripts/vscripts/ui/menu_ns_custom_match_settings_categories.nut b/Northstar.Client/mod/scripts/vscripts/ui/menu_ns_custom_match_settings_categories.nut
new file mode 100644
index 00000000..711cbbbc
--- /dev/null
+++ b/Northstar.Client/mod/scripts/vscripts/ui/menu_ns_custom_match_settings_categories.nut
@@ -0,0 +1,68 @@
+global function AddNorthstarCustomMatchSettingsCategoryMenu
+
+void function AddNorthstarCustomMatchSettingsCategoryMenu()
+{
+ AddMenu( "CustomMatchSettingsCategoryMenu", $"resource/ui/menus/custom_match_settings_categories.menu", InitNorthstarCustomMatchSettingsCategoryMenu, "#MENU_MATCH_SETTINGS" )
+}
+
+void function InitNorthstarCustomMatchSettingsCategoryMenu()
+{
+ AddMenuEventHandler( GetMenu( "CustomMatchSettingsCategoryMenu" ), eUIEvent.MENU_OPEN, OnNorthstarCustomMatchSettingsCategoryMenuOpened )
+ AddMenuFooterOption( GetMenu( "CustomMatchSettingsCategoryMenu" ), BUTTON_B, "#B_BUTTON_BACK", "#BACK" )
+ AddMenuFooterOption( GetMenu( "CustomMatchSettingsCategoryMenu" ), BUTTON_Y, "#Y_BUTTON_RESTORE_DEFAULTS", "#RESTORE_DEFAULTS", ResetMatchSettingsToDefault )
+
+ foreach ( var button in GetElementsByClassname( GetMenu( "CustomMatchSettingsCategoryMenu" ), "MatchSettingCategoryButton" ) )
+ {
+ AddButtonEventHandler( button, UIE_CLICK, SelectPrivateMatchSettingsCategory )
+
+ Hud_SetEnabled( button, false )
+ Hud_SetVisible( button, false )
+ }
+}
+
+void function OnNorthstarCustomMatchSettingsCategoryMenuOpened()
+{
+ array<string> categories = GetPrivateMatchSettingCategories()
+ array<var> buttons = GetElementsByClassname( GetMenu( "CustomMatchSettingsCategoryMenu" ), "MatchSettingCategoryButton" )
+
+ for ( int i = 0, j = 0; j < categories.len() && i < buttons.len(); i++, j++ )
+ {
+ Hud_SetEnabled( buttons[ i ], false )
+ Hud_SetVisible( buttons[ i ], false )
+
+ // skip gamemode/playlist categories for modes that aren't the current one
+ // todo this fucking breaks everything lmao
+ //bool gamemode = categories[ j ].find( "#GAMEMODE_" ) == 0
+ //if ( gamemode || categories[ j ].find( "#PL_" ) == 0 )
+ //{
+ // if ( gamemode )
+ // {
+ // if ( categories[ j ].slice( 10 ) != PrivateMatch_GetSelectedMode() )
+ // {
+ // i--
+ // continue
+ // }
+ // }
+ // else if ( categories[ j ].slice( 4 ) != PrivateMatch_GetSelectedMode() )
+ // {
+ // i--
+ // continue
+ // }
+ //}
+
+ Hud_SetText( buttons[ i ], Localize( categories[ j ] ) + " ->" )
+ Hud_SetEnabled( buttons[ i ], true )
+ Hud_SetVisible( buttons[ i ], true )
+ }
+}
+
+void function SelectPrivateMatchSettingsCategory( var button )
+{
+ SetNextMatchSettingsCategory( GetPrivateMatchSettingCategories()[ int( Hud_GetScriptID( button ) ) ] )
+ AdvanceMenu( GetMenu( "CustomMatchSettingsMenu" ) )
+}
+
+void function ResetMatchSettingsToDefault( var button )
+{
+ ClientCommand( "ResetMatchSettingsToDefault" )
+} \ No newline at end of file
diff --git a/Northstar.Client/mod/scripts/vscripts/ui/menu_ns_modmenu.nut b/Northstar.Client/mod/scripts/vscripts/ui/menu_ns_modmenu.nut
new file mode 100644
index 00000000..4a56891e
--- /dev/null
+++ b/Northstar.Client/mod/scripts/vscripts/ui/menu_ns_modmenu.nut
@@ -0,0 +1,108 @@
+global function AddNorthstarModMenu
+global function AddNorthstarModMenu_MainMenuFooter
+
+void function AddNorthstarModMenu()
+{
+ AddMenu( "ModListMenu", $"resource/ui/menus/modlist.menu", InitModMenu )
+}
+
+void function AddNorthstarModMenu_MainMenuFooter()
+{
+ AddMenuFooterOption( GetMenu( "MainMenu" ), BUTTON_Y, "#Y_MENU_TITLE_MODS", "#MENU_TITLE_MODS", AdvanceToModListMenu )
+}
+
+void function AdvanceToModListMenu( var button )
+{
+ AdvanceMenu( GetMenu( "ModListMenu" ) )
+}
+
+void function InitModMenu()
+{
+ var menu = GetMenu( "ModListMenu" )
+
+ AddMenuEventHandler( menu, eUIEvent.MENU_OPEN, OnModMenuOpened )
+ AddMenuFooterOption( menu, BUTTON_B, "#B_BUTTON_BACK", "#BACK" )
+ AddMenuFooterOption( menu, BUTTON_Y, "#Y_RELOAD_MODS", "#RELOAD_MODS", ReloadMods )
+
+ foreach ( var button in GetElementsByClassname( GetMenu( "ModListMenu" ), "ModButton" ) )
+ AddButtonEventHandler( button, UIE_GET_FOCUS, OnModMenuButtonFocused )
+}
+
+void function OnModMenuOpened()
+{
+ Hud_SetText( Hud_GetChild( GetMenu( "ModListMenu" ), "Title" ), "#MENU_TITLE_MODS" )
+
+ array<var> buttons = GetElementsByClassname( GetMenu( "ModListMenu" ), "ModButton" )
+
+ // disable all buttons, we'll enable the ones we need later
+ foreach ( var button in buttons )
+ {
+ Hud_SetEnabled( button, false )
+ Hud_SetVisible( button, false )
+ }
+
+ array<string> modNames = NSGetModNames()
+ for ( int i = 0; i < modNames.len() && i < buttons.len(); i++ )
+ {
+ Hud_SetEnabled( buttons[ i ], true )
+ Hud_SetVisible( buttons[ i ], true )
+
+ SetButtonRuiText( buttons[ i ], modNames[ i ] + " v" + NSGetModVersionByModName( modNames[ i ] ) )
+ }
+}
+
+void function OnModMenuButtonFocused( var button )
+{
+ string modName = NSGetModNames()[ int ( Hud_GetScriptID( button ) ) ]
+
+ var rui = Hud_GetRui( Hud_GetChild( GetMenu( "ModListMenu" ), "LabelDetails" ) )
+
+ RuiSetGameTime( rui, "startTime", -99999.99 ) // make sure it skips the whole animation for showing this
+ RuiSetString( rui, "headerText", modName )
+ RuiSetString( rui, "messageText", FormatModDescription( modName ) )
+}
+
+string function FormatModDescription( string modName )
+{
+ string ret
+ // version
+ ret += format( "Version %s\n", NSGetModVersionByModName( modName ) )
+
+ // download link
+ string modLink = NSGetModDownloadLinkByModName( modName )
+ if ( modLink.len() != 0 )
+ ret += format( "Download link: \"%s\"\n", modLink )
+
+ // load priority
+ ret += format( "Load Priority: %i\n", NSGetModLoadPriority( modName ) )
+
+ // todo: add ClientRequired here
+
+ // convars
+ array<string> modCvars = NSGetModConvarsByModName( modName )
+ if ( modCvars.len() != 0 )
+ {
+ ret += "ConVars: "
+
+ for ( int i = 0; i < modCvars.len(); i++ )
+ {
+ if ( i != modCvars.len() - 1 )
+ ret += format( "\"%s\", ", modCvars[ i ] )
+ else
+ ret += format( "\"%s\"", modCvars[ i ] )
+ }
+
+ ret += "\n"
+ }
+
+ // description
+ ret += format( "\n%s\n", NSGetModDescriptionByModName( modName ) )
+
+ return ret
+}
+
+void function ReloadMods( var button )
+{
+ NSReloadMods()
+ OnModMenuOpened() // temp, until we start doing uiscript_reset here
+} \ No newline at end of file
diff --git a/Northstar.Client/mod/scripts/vscripts/ui/menu_ns_serverbrowser.nut b/Northstar.Client/mod/scripts/vscripts/ui/menu_ns_serverbrowser.nut
new file mode 100644
index 00000000..bbeb0d0b
--- /dev/null
+++ b/Northstar.Client/mod/scripts/vscripts/ui/menu_ns_serverbrowser.nut
@@ -0,0 +1,198 @@
+global function AddNorthstarServerBrowserMenu
+global function ThreadedAuthAndConnectToServer
+
+const int BUTTONS_PER_PAGE = 15
+
+struct {
+ int page = 0
+ int lastSelectedServer = 0
+} file
+
+void function AddNorthstarServerBrowserMenu()
+{
+ AddMenu( "ServerBrowserMenu", $"resource/ui/menus/server_browser.menu", InitServerBrowserMenu, "#MENU_SERVER_BROWSER" )
+}
+
+void function InitServerBrowserMenu()
+{
+ AddMenuEventHandler( GetMenu( "ServerBrowserMenu" ), eUIEvent.MENU_OPEN, OnServerBrowserMenuOpened )
+ AddMenuFooterOption( GetMenu( "ServerBrowserMenu" ), BUTTON_B, "#B_BUTTON_BACK", "#BACK" )
+ AddMenuFooterOption( GetMenu( "ServerBrowserMenu" ), BUTTON_Y, "#Y_REFRESH_SERVERS", "#REFRESH_SERVERS", RefreshServers )
+
+ foreach ( var button in GetElementsByClassname( GetMenu( "ServerBrowserMenu" ), "ServerButton" ) )
+ {
+ AddButtonEventHandler( button, UIE_GET_FOCUS, OnServerFocused )
+ AddButtonEventHandler( button, UIE_CLICK, OnServerSelected )
+ }
+}
+
+void function OnServerBrowserMenuOpened()
+{
+ Hud_SetText( Hud_GetChild( GetMenu( "ServerBrowserMenu" ), "Title" ), "#MENU_TITLE_SERVER_BROWSER" )
+ UI_SetPresentationType( ePresentationType.KNOWLEDGEBASE_MAIN )
+
+ file.page = 0
+ // dont rerequest if we came from the connect menu
+ if ( !NSIsRequestingServerList() && uiGlobal.lastMenuNavDirection != MENU_NAV_BACK )
+ {
+ NSClearRecievedServerList()
+ NSRequestServerList()
+ }
+
+ thread WaitForServerListRequest()
+}
+
+void function RefreshServers( var button )
+{
+ if ( NSIsRequestingServerList() )
+ return
+
+ NSClearRecievedServerList()
+ NSRequestServerList()
+
+ thread WaitForServerListRequest()
+}
+
+void function WaitForServerListRequest()
+{
+ var menu = GetMenu( "ServerBrowserMenu" )
+ array<var> serverButtons = GetElementsByClassname( menu, "ServerButton" )
+ foreach ( var button in serverButtons )
+ {
+ Hud_SetEnabled( button, false )
+ Hud_SetVisible( button, false )
+ }
+
+ Hud_SetVisible( Hud_GetChild( menu, "LabelDetails" ), false )
+ Hud_SetVisible( Hud_GetChild( menu, "NextMapImage" ), false )
+ Hud_SetVisible( Hud_GetChild( menu, "NextMapName" ), false )
+ Hud_SetVisible( Hud_GetChild( menu, "NextModeIcon" ), false )
+ Hud_SetVisible( Hud_GetChild( menu, "NextGameModeName" ), false )
+
+ Hud_SetEnabled( serverButtons[ 0 ], true )
+ Hud_SetVisible( serverButtons[ 0 ], true )
+
+ SetButtonRuiText( serverButtons[ 0 ], "#NS_SERVERBROWSER_WAITINGFORSERVERS" )
+
+ // wait for request to complete
+ while ( NSIsRequestingServerList() )
+ WaitFrame()
+
+ if ( !NSMasterServerConnectionSuccessful() )
+ SetButtonRuiText( serverButtons[ 0 ], "#NS_SERVERBROWSER_CONNECTIONFAILED" )
+ else
+ UpdateShownPage()
+}
+
+void function UpdateShownPage()
+{
+ var menu = GetMenu( "ServerBrowserMenu" )
+
+ // hide old ui elements
+ array<var> serverButtons = GetElementsByClassname( menu, "ServerButton" )
+ foreach ( var button in serverButtons )
+ {
+ Hud_SetEnabled( button, false )
+ Hud_SetVisible( button, false )
+ }
+
+ Hud_SetFocused( serverButtons[ serverButtons.len() - 1 ] )
+
+ Hud_SetVisible( Hud_GetChild( menu, "LabelDetails" ), false )
+ Hud_SetVisible( Hud_GetChild( menu, "NextMapImage" ), false )
+ Hud_SetVisible( Hud_GetChild( menu, "NextMapName" ), false )
+ Hud_SetVisible( Hud_GetChild( menu, "NextModeIcon" ), false )
+ Hud_SetVisible( Hud_GetChild( menu, "NextGameModeName" ), false )
+
+ for ( int i = 0; i < NSGetServerCount() && i < serverButtons.len(); i++ )
+ {
+ int serverIndex = file.page * BUTTONS_PER_PAGE + i
+
+ Hud_SetEnabled( serverButtons[ i ], true )
+ Hud_SetVisible( serverButtons[ i ], true )
+ SetButtonRuiText( serverButtons[ i ], NSGetServerName( serverIndex ) )
+ }
+
+ if ( NSGetServerCount() == 0 )
+ {
+ Hud_SetEnabled( serverButtons[ 0 ], true )
+ Hud_SetVisible( serverButtons[ 0 ], true )
+ SetButtonRuiText( serverButtons[ 0 ], "#NS_SERVERBROWSER_NOSERVERS" )
+ }
+}
+
+void function OnServerFocused( var button )
+{
+ if ( NSIsRequestingServerList() || !NSMasterServerConnectionSuccessful() || NSGetServerCount() == 0 )
+ return
+
+ var menu = GetMenu( "ServerBrowserMenu" )
+ int serverIndex = file.page * BUTTONS_PER_PAGE + int ( Hud_GetScriptID( button ) )
+
+ // text panel
+ Hud_SetVisible( Hud_GetChild( menu, "LabelDetails" ), true )
+ var textRui = Hud_GetRui( Hud_GetChild( menu, "LabelDetails" ) )
+ RuiSetGameTime( textRui, "startTime", -99999.99 ) // make sure it skips the whole animation for showing this
+ RuiSetString( textRui, "messageText", FormatServerDescription( serverIndex ) )
+
+ // map name/image
+ string map = NSGetServerMap( serverIndex )
+ Hud_SetVisible( Hud_GetChild( menu, "NextMapImage" ), true )
+ RuiSetImage( Hud_GetRui( Hud_GetChild( menu, "NextMapImage" ) ), "basicImage", GetMapImageForMapName( map ) )
+ Hud_SetVisible( Hud_GetChild( menu, "NextMapName" ), true )
+ Hud_SetText( Hud_GetChild( menu, "NextMapName" ), GetMapDisplayName( map ) )
+
+ // mode name/image
+ string mode = NSGetServerPlaylist( serverIndex )
+ Hud_SetVisible( Hud_GetChild( menu, "NextModeIcon" ), true )
+ RuiSetImage( Hud_GetRui( Hud_GetChild( menu, "NextModeIcon" ) ), "basicImage", GetPlaylistThumbnailImage( mode ) )
+ Hud_SetVisible( Hud_GetChild( menu, "NextGameModeName" ), true )
+
+ string displayName = GetGameModeDisplayName( mode )
+ if ( displayName.len() != 0 )
+ Hud_SetText( Hud_GetChild( menu, "NextGameModeName" ), displayName )
+ else
+ Hud_SetText( Hud_GetChild( menu, "NextGameModeName" ), "#NS_SERVERBROWSER_UNKNOWNMODE" )
+}
+
+string function FormatServerDescription( int server )
+{
+ string ret = "\n\n\n\n"
+
+ ret += NSGetServerName( server ) + "\n"
+ ret += format( "%i/%i players\n", NSGetServerPlayerCount( server ), NSGetServerMaxPlayerCount( server ) )
+ ret += NSGetServerDescription( server ) + "\n"
+
+ return ret
+}
+
+void function OnServerSelected( var button )
+{
+ if ( NSIsRequestingServerList() || !NSMasterServerConnectionSuccessful() )
+ return
+
+ var menu = GetMenu( "ServerBrowserMenu" )
+ int serverIndex = file.page * BUTTONS_PER_PAGE + int ( Hud_GetScriptID( button ) )
+
+ file.lastSelectedServer = serverIndex
+
+ if ( NSServerRequiresPassword( serverIndex ) )
+ AdvanceMenu( GetMenu( "ConnectWithPasswordMenu" ) )
+ else
+ thread ThreadedAuthAndConnectToServer()
+}
+
+void function ThreadedAuthAndConnectToServer( string password = "" )
+{
+ if ( NSIsAuthenticatingWithServer() )
+ return
+
+ print( "trying to authenticate with server " + NSGetServerName( file.lastSelectedServer ) + " with password " + password )
+ NSTryAuthWithServer( file.lastSelectedServer, password )
+
+ while ( NSIsAuthenticatingWithServer() )
+ WaitFrame()
+
+ if ( NSWasAuthSuccessful() )
+ NSConnectToAuthedServer()
+} \ No newline at end of file
diff --git a/Northstar.Client/mod/scripts/vscripts/ui/menu_private_match.nut b/Northstar.Client/mod/scripts/vscripts/ui/menu_private_match.nut
new file mode 100644
index 00000000..b1da22ee
--- /dev/null
+++ b/Northstar.Client/mod/scripts/vscripts/ui/menu_private_match.nut
@@ -0,0 +1,804 @@
+untyped
+
+global function MenuPrivateMatch_Init
+
+global function InitPrivateMatchMenu
+
+global function HandleLockedCustomMenuItem
+global function GetMapImageForMapName
+
+struct
+{
+ var menu
+
+ array matchStatusRuis
+
+ array MMDevStringElems
+
+ array teamSlotBackgrounds
+ array teamSlotBackgroundsNeutral
+
+ var enemyTeamBackgroundPanel
+ var friendlyTeamBackgroundPanel
+ var enemyTeamBackground
+ var friendlyTeamBackground
+ var enemyPlayersPanel
+ var friendlyPlayersPanel
+
+ var listFriendlies
+ var listEnemies
+
+ var nextMapNameLabel
+ var nextGameModeLabel
+
+ var inviteRoomButton
+ var inviteFriendsButton
+
+ int inboxHeaderIndex
+
+ int customizeHeaderIndex
+ var pilotButton
+ var titanButton
+ var boostsButton
+ var storeButton
+ var factionButton
+ var bannerButton
+ var patchButton
+ var statsButton
+ var dpadCommsButton
+
+ var playHeader
+ var customizeHeader
+ var callsignHeader
+ var storeHeader
+
+ var startMatchButton
+ var selectMapButton
+ var selectModeButton
+ var matchSettingsButton
+
+ var callsignCard
+
+ var spectatorLabel
+
+ var matchSettingsPanel
+
+ ComboStruct &lobbyComboStruct
+} file
+
+const table<asset> mapImages =
+{
+ mp_forwardbase_kodai = $"loadscreens/mp_forwardbase_kodai_lobby",
+ mp_grave = $"loadscreens/mp_grave_lobby",
+ mp_homestead = $"loadscreens/mp_homestead_lobby",
+ mp_thaw = $"loadscreens/mp_thaw_lobby",
+ mp_black_water_canal = $"loadscreens/mp_black_water_canal_lobby",
+ mp_eden = $"loadscreens/mp_eden_lobby",
+ mp_drydock = $"loadscreens/mp_drydock_lobby",
+ mp_crashsite3 = $"loadscreens/mp_crashsite3_lobby",
+ mp_complex3 = $"loadscreens/mp_complex3_lobby",
+ mp_angel_city = $"loadscreens/mp_angle_city_r2_lobby",
+ mp_colony02 = $"loadscreens/mp_colony02_lobby",
+ mp_glitch = $"loadscreens/mp_glitch_lobby",
+ mp_lf_stacks = $"loadscreens/mp_stacks_lobby",
+ mp_lf_meadow = $"loadscreens/mp_meadow_lobby",
+ mp_lf_deck = $"loadscreens/mp_lf_deck_lobby",
+ mp_lf_traffic = $"loadscreens/mp_lf_traffic_lobby",
+ mp_coliseum = $"loadscreens/mp_coliseum_lobby",
+ mp_coliseum_column = $"loadscreens/mp_coliseum_column_lobby",
+ mp_relic02 = $"loadscreens/mp_relic02_lobby",
+ mp_wargames = $"loadscreens/mp_wargames_lobby",
+ mp_rise = $"loadscreens/mp_rise_lobby",
+ mp_lf_township = $"loadscreens/mp_lf_township_lobby",
+ mp_lf_uma = $"loadscreens/mp_lf_uma_lobby",
+}
+
+void function MenuPrivateMatch_Init()
+{
+ PrecacheHUDMaterial( $"ui/menu/common/menu_background_neutral" )
+ PrecacheHUDMaterial( $"ui/menu/common/menu_background_imc" )
+ PrecacheHUDMaterial( $"ui/menu/common/menu_background_militia" )
+ PrecacheHUDMaterial( $"ui/menu/common/menu_background_imc_blur" )
+ PrecacheHUDMaterial( $"ui/menu/common/menu_background_militia_blur" )
+ PrecacheHUDMaterial( $"ui/menu/common/menu_background_neutral_blur" )
+ PrecacheHUDMaterial( $"ui/menu/common/menu_background_blackMarket" )
+ PrecacheHUDMaterial( $"ui/menu/rank_menus/ranked_FE_background" )
+
+ PrecacheHUDMaterial( $"ui/menu/lobby/friendly_slot" )
+ PrecacheHUDMaterial( $"ui/menu/lobby/friendly_player" )
+ PrecacheHUDMaterial( $"ui/menu/lobby/enemy_slot" )
+ PrecacheHUDMaterial( $"ui/menu/lobby/enemy_player" )
+ PrecacheHUDMaterial( $"ui/menu/lobby/neutral_slot" )
+ PrecacheHUDMaterial( $"ui/menu/lobby/neutral_player" )
+ PrecacheHUDMaterial( $"ui/menu/lobby/player_hover" )
+ PrecacheHUDMaterial( $"ui/menu/main_menu/motd_background" )
+ PrecacheHUDMaterial( $"ui/menu/main_menu/motd_background_happyhour" )
+
+ AddUICallback_OnLevelInit( OnPrivateLobbyLevelInit )
+}
+
+asset function GetMapImageForMapName( string mapName )
+{
+ if ( mapName in mapImages )
+ return mapImages[mapName]
+
+ // no way to convert string => asset for dynamic stuff so
+ // pain
+ return expect asset ( compilestring( "return $\"loadscreens/" + mapName + "_lobby\"" )() )
+}
+
+
+void function InitPrivateMatchMenu()
+{
+ var menu = GetMenu( "PrivateLobbyMenu" )
+ file.menu = menu
+
+ AddMenuEventHandler( menu, eUIEvent.MENU_OPEN, OnPrivateMatchMenu_Open )
+ AddMenuEventHandler( menu, eUIEvent.MENU_CLOSE, OnLobbyMenu_Close )
+ AddMenuEventHandler( menu, eUIEvent.MENU_NAVIGATE_BACK, OnLobbyMenu_NavigateBack )
+
+ file.startMatchButton = Hud_GetChild( menu, "StartMatchButton" )
+ Hud_AddEventHandler( file.startMatchButton, UIE_CLICK, OnStartMatchButton_Activate )
+
+ RegisterUIVarChangeCallback( "privatematch_map", Privatematch_map_Changed )
+ RegisterUIVarChangeCallback( "privatematch_mode", Privatematch_mode_Changed )
+ RegisterUIVarChangeCallback( "privatematch_starting", Privatematch_starting_Changed )
+ RegisterUIVarChangeCallback( "gameStartTime", GameStartTime_Changed )
+
+ file.matchStatusRuis = GetElementsByClassnameForMenus( "MatchmakingStatusRui", uiGlobal.allMenus )
+ file.MMDevStringElems = GetElementsByClassnameForMenus( "MMDevStringClass", uiGlobal.allMenus )
+
+ file.friendlyPlayersPanel = Hud_GetChild( menu, "MatchFriendliesPanel" )
+ file.enemyPlayersPanel = Hud_GetChild( menu, "MatchEnemiesPanel" )
+
+ file.listFriendlies = Hud_GetChild( file.friendlyPlayersPanel, "ListFriendlies" )
+ file.listEnemies = Hud_GetChild( file.enemyPlayersPanel, "ListEnemies" )
+
+ file.friendlyTeamBackgroundPanel = Hud_GetChild( file.friendlyPlayersPanel, "LobbyFriendlyTeamBackground" )
+ file.enemyTeamBackgroundPanel = Hud_GetChild( file.enemyPlayersPanel, "LobbyEnemyTeamBackground" )
+
+#if PC_PROG
+ var panelSize = Hud_GetSize( file.enemyPlayersPanel )
+ Hud_SetSize( Hud_GetChild( menu, "LobbyChatBox" ), panelSize[0], panelSize[1] )
+#endif // #if PC_PROG
+
+ file.friendlyTeamBackground = Hud_GetChild( file.friendlyTeamBackgroundPanel, "TeamBackground" )
+ file.enemyTeamBackground = Hud_GetChild( file.enemyTeamBackgroundPanel, "TeamBackground" )
+
+ file.teamSlotBackgrounds = GetElementsByClassnameForMenus( "LobbyTeamSlotBackgroundClass", uiGlobal.allMenus )
+ file.teamSlotBackgroundsNeutral = GetElementsByClassnameForMenus( "LobbyTeamSlotBackgroundNeutralClass", uiGlobal.allMenus )
+
+ file.nextMapNameLabel = Hud_GetChild( menu, "NextMapName" )
+ file.nextGameModeLabel = Hud_GetChild( menu, "NextGameModeName" )
+
+ file.callsignCard = Hud_GetChild( menu, "CallsignCard" )
+
+ file.spectatorLabel = Hud_GetChild( menu, "SpectatorLabel" )
+
+ file.matchSettingsPanel = Hud_GetChild( menu, "MatchSettings" )
+
+ SetupComboButtons( menu, file.startMatchButton, file.listFriendlies )
+
+ AddMenuFooterOption( menu, BUTTON_A, "#A_BUTTON_SELECT", "" )
+ AddMenuFooterOption( menu, BUTTON_B, "#B_BUTTON_BACK", "#BACK" )
+
+ AddMenuFooterOption( menu, BUTTON_Y, "#Y_BUTTON_SWITCH_TEAMS", "#SWITCH_TEAMS", PCSwitchTeamsButton_Activate, CanSwitchTeams )
+ AddMenuFooterOption( menu, BUTTON_X, "#X_BUTTON_MUTE", "#MOUSE2_MUTE", null, CanMute )
+ AddMenuFooterOption( menu, BUTTON_SHOULDER_RIGHT, "#RB_TRIGGER_TOGGLE_SPECTATE", "#SPECTATE_TEAM", PCToggleSpectateButton_Activate, CanSwitchTeams )
+
+ AddMenuVarChangeHandler( "focus", UpdateFooterOptions )
+ AddMenuVarChangeHandler( "isFullyConnected", UpdateFooterOptions )
+ AddMenuVarChangeHandler( "isPartyLeader", UpdateFooterOptions )
+ AddMenuVarChangeHandler( "isPrivateMatch", UpdateFooterOptions )
+ #if DURANGO_PROG
+ AddMenuVarChangeHandler( "DURANGO_canInviteFriends", UpdateFooterOptions )
+ AddMenuVarChangeHandler( "DURANGO_isJoinable", UpdateFooterOptions )
+ AddMenuVarChangeHandler( "DURANGO_isGameFullyInstalled", UpdateFooterOptions )
+ #elseif PS4_PROG
+ AddMenuVarChangeHandler( "PS4_canInviteFriends", UpdateFooterOptions )
+ #elseif PC_PROG
+ AddMenuVarChangeHandler( "ORIGIN_isEnabled", UpdateFooterOptions )
+ AddMenuVarChangeHandler( "ORIGIN_isJoinable", UpdateFooterOptions )
+ #endif
+}
+
+
+void function OnSelectMapButton_Activate( var button )
+{
+ if ( Hud_IsLocked( button ) )
+ return
+
+ AdvanceMenu( GetMenu( "MapsMenu" ) )
+}
+
+void function OnSelectModeButton_Activate( var button )
+{
+ if ( Hud_IsLocked( button ) )
+ return
+
+ AdvanceMenu( GetMenu( "ModesMenu" ) )
+}
+
+void function OnSelectMatchSettings_Activate( var button )
+{
+ if ( Hud_IsLocked( button ) )
+ return
+
+ if ( !IsNorthstarServer() )
+ AdvanceMenu( GetMenu( "MatchSettingsMenu" ) )
+ else
+ AdvanceMenu( GetMenu( "CustomMatchSettingsCategoryMenu" ) )
+}
+
+void function SetupComboButtons( var menu, var navUpButton, var navDownButton )
+{
+ ComboStruct comboStruct = ComboButtons_Create( menu )
+ file.lobbyComboStruct = comboStruct
+
+ comboStruct.navUpButton = navUpButton
+ comboStruct.navDownButton = navDownButton
+
+ int headerIndex = 0
+ int buttonIndex = 0
+ file.playHeader = AddComboButtonHeader( comboStruct, headerIndex, "#MENU_HEADER_PRIVATE_MATCH" )
+ var selectModeButton = AddComboButton( comboStruct, headerIndex, buttonIndex++, "#MENU_TITLE_SELECT_MODE" )
+ file.selectModeButton = selectModeButton
+ Hud_AddEventHandler( selectModeButton, UIE_CLICK, OnSelectModeButton_Activate )
+ var selectMapButton = AddComboButton( comboStruct, headerIndex, buttonIndex++, "#MENU_TITLE_SELECT_MAP" )
+ file.selectMapButton = selectMapButton
+ Hud_AddEventHandler( selectMapButton, UIE_CLICK, OnSelectMapButton_Activate )
+
+ file.matchSettingsButton = AddComboButton( comboStruct, headerIndex, buttonIndex++, "#MENU_TITLE_MATCH_SETTINGS" )
+ Hud_AddEventHandler( file.matchSettingsButton, UIE_CLICK, OnSelectMatchSettings_Activate )
+
+ var friendsButton = AddComboButton( comboStruct, headerIndex, buttonIndex++, "#MENU_TITLE_INVITE_FRIENDS" )
+ file.inviteFriendsButton = friendsButton
+ Hud_AddEventHandler( friendsButton, UIE_CLICK, InviteFriendsIfAllowed )
+
+ headerIndex++
+ buttonIndex = 0
+ file.customizeHeader = AddComboButtonHeader( comboStruct, headerIndex, "#MENU_HEADER_LOADOUTS" )
+ file.customizeHeaderIndex = headerIndex
+ var pilotButton = AddComboButton( comboStruct, headerIndex, buttonIndex++, "#MENU_TITLE_PILOT" )
+ file.pilotButton = pilotButton
+
+ Hud_AddEventHandler( pilotButton, UIE_CLICK, AdvanceMenuEventHandler( GetMenu( "EditPilotLoadoutsMenu" ) ) )
+ var titanButton = AddComboButton( comboStruct, headerIndex, buttonIndex++, "#MENU_TITLE_TITAN" )
+ file.titanButton = titanButton
+ Hud_AddEventHandler( titanButton, UIE_CLICK, AdvanceMenuEventHandler( GetMenu( "EditTitanLoadoutsMenu" ) ) )
+ file.boostsButton = AddComboButton( comboStruct, headerIndex, buttonIndex++, "#MENU_TITLE_BOOSTS" )
+ Hud_AddEventHandler( file.boostsButton, UIE_CLICK, AdvanceMenuEventHandler( GetMenu( "BurnCardMenu" ) ) )
+ file.dpadCommsButton = AddComboButton( comboStruct, headerIndex, buttonIndex++, "#MENU_TITLE_COMMS" )
+ Hud_AddEventHandler( file.dpadCommsButton, UIE_CLICK, OnDpadCommsButton_Activate )
+
+ headerIndex++
+ buttonIndex = 0
+ file.callsignHeader = AddComboButtonHeader( comboStruct, headerIndex, "#MENU_HEADER_CALLSIGN" )
+ file.bannerButton = AddComboButton( comboStruct, headerIndex, buttonIndex++, "#MENU_TITLE_BANNER" )
+ Hud_AddEventHandler( file.bannerButton, UIE_CLICK, AdvanceMenuEventHandler( GetMenu( "CallsignCardSelectMenu" ) ) )
+ file.patchButton = AddComboButton( comboStruct, headerIndex, buttonIndex++, "#MENU_TITLE_PATCH" )
+ Hud_AddEventHandler( file.patchButton, UIE_CLICK, AdvanceMenuEventHandler( GetMenu( "CallsignIconSelectMenu" ) ) )
+ file.factionButton = AddComboButton( comboStruct, headerIndex, buttonIndex++, "#MENU_TITLE_FACTION" )
+ Hud_AddEventHandler( file.factionButton, UIE_CLICK, AdvanceMenuEventHandler( GetMenu( "FactionChoiceMenu" ) ) )
+ file.statsButton = AddComboButton( comboStruct, headerIndex, buttonIndex++, "#MENU_TITLE_STATS" )
+ Hud_AddEventHandler( file.statsButton, UIE_CLICK, AdvanceMenuEventHandler( GetMenu( "ViewStatsMenu" ) ) )
+
+ file.callsignCard = Hud_GetChild( menu, "CallsignCard" )
+
+ headerIndex++
+ buttonIndex = 0
+ file.storeHeader = AddComboButtonHeader( comboStruct, headerIndex, "#MENU_HEADER_STORE" )
+ file.storeButton = AddComboButton( comboStruct, headerIndex, buttonIndex++, "#MENU_TITLE_STORE_BROWSE" )
+ Hud_AddEventHandler( file.storeButton, UIE_CLICK, OnStoreButton_Activate )
+ var storeNewReleasesButton = AddComboButton( comboStruct, headerIndex, buttonIndex++, "#MENU_TITLE_STORE_NEW_RELEASES" )
+ Hud_AddEventHandler( storeNewReleasesButton, UIE_CLICK, OnStoreNewReleasesButton_Activate )
+ var storeBundlesButton = AddComboButton( comboStruct, headerIndex, buttonIndex++, "#MENU_TITLE_STORE_BUNDLES" )
+ Hud_AddEventHandler( storeBundlesButton, UIE_CLICK, OnStoreBundlesButton_Activate )
+
+ headerIndex++
+ buttonIndex = 0
+ var settingsHeader = AddComboButtonHeader( comboStruct, headerIndex, "#MENU_HEADER_SETTINGS" )
+ var controlsButton = AddComboButton( comboStruct, headerIndex, buttonIndex++, "#MENU_TITLE_CONTROLS" )
+ Hud_AddEventHandler( controlsButton, UIE_CLICK, AdvanceMenuEventHandler( GetMenu( "ControlsMenu" ) ) )
+ #if CONSOLE_PROG
+ var avButton = AddComboButton( comboStruct, headerIndex, buttonIndex++, "#AUDIO_VIDEO" )
+ Hud_AddEventHandler( avButton, UIE_CLICK, AdvanceMenuEventHandler( GetMenu( "AudioVideoMenu" ) ) )
+ #elseif PC_PROG
+ var videoButton = AddComboButton( comboStruct, headerIndex, buttonIndex++, "#AUDIO" )
+ Hud_AddEventHandler( videoButton, UIE_CLICK, AdvanceMenuEventHandler( GetMenu( "AudioMenu" ) ) )
+ var soundButton = AddComboButton( comboStruct, headerIndex, buttonIndex++, "#VIDEO" )
+ Hud_AddEventHandler( soundButton, UIE_CLICK, AdvanceMenuEventHandler( GetMenu( "VideoMenu" ) ) )
+ #endif
+ var knbButton = AddComboButton( comboStruct, headerIndex, buttonIndex++, "#KNB_MENU_HEADER" )
+ Hud_AddEventHandler( knbButton, UIE_CLICK, AdvanceMenuEventHandler( GetMenu( "KnowledgeBaseMenu" ) ) )
+
+ ComboButtons_Finalize( comboStruct )
+}
+
+
+bool function IsPlayerListFocused()
+{
+ var focusedItem = GetFocus()
+
+ // The check for GetScriptID existing isn't ideal, but if the text chat text output element has focus it will script error otherwise
+ return ( (focusedItem != null) && ("GetScriptID" in focusedItem) && (Hud_GetScriptID( focusedItem ) == "PlayerListButton") )
+}
+
+bool function MatchResultsExist()
+{
+ return true // TODO
+}
+
+bool function CanSwitchTeams()
+{
+ return ( GetMenuVarBool( "isPrivateMatch" ) && ( level.ui.privatematch_starting != ePrivateMatchStartState.STARTING ) )
+}
+
+bool function CanMute()
+{
+ return IsPlayerListFocused()
+}
+
+void function OnLobbyMenu_Open()
+{
+ Assert( IsConnected() )
+
+ UpdatePrivateMatchButtons()
+
+ thread UpdateLobbyUI()
+ thread LobbyMenuUpdate( GetMenu( "PrivateLobbyMenu" ) )
+
+ if ( uiGlobal.activeMenu == GetMenu( "PrivateLobbyMenu" ) )
+ UI_SetPresentationType( ePresentationType.NO_MODELS )
+
+ if ( IsFullyConnected() )
+ {
+ entity player = GetUIPlayer()
+ if ( !IsValid( player ) )
+ return
+
+ while ( player.GetPersistentVarAsInt( "initializedVersion" ) < PERSISTENCE_INIT_VERSION )
+ {
+ WaitFrame()
+ }
+
+ UpdateCallsignElement( file.callsignCard )
+ RefreshCreditsAvailable()
+
+ bool emotesAreEnabled = EmotesEnabled()
+ // "Customize"
+ {
+ bool anyNewPilotItems = HasAnyNewPilotItems( player )
+ bool anyNewTitanItems = HasAnyNewTitanItems( player )
+ bool anyNewBoosts = HasAnyNewBoosts( player )
+ bool anyNewCommsIcons = false // emotesAreEnabled ? HasAnyNewDpadCommsIcons( player ) : false
+ bool anyNewCustomizeHeader = (anyNewPilotItems || anyNewTitanItems || anyNewBoosts || anyNewCommsIcons)
+
+ RuiSetBool( Hud_GetRui( file.customizeHeader ), "isNew", anyNewCustomizeHeader )
+ ComboButton_SetNew( file.pilotButton, anyNewPilotItems )
+ ComboButton_SetNew( file.titanButton, anyNewTitanItems )
+ ComboButton_SetNew( file.boostsButton, anyNewBoosts )
+ ComboButton_SetNew( file.dpadCommsButton, anyNewCommsIcons )
+
+ if ( !emotesAreEnabled )
+ {
+ Hud_Hide( file.dpadCommsButton )
+ ComboButtons_ResetColumnFocus( file.lobbyComboStruct )
+ }
+ else
+ {
+ Hud_Show( file.dpadCommsButton )
+ }
+ }
+
+ // "Store"
+ {
+ bool storeIsNew = DLCStoreShouldBeMarkedAsNew()
+ RuiSetBool( Hud_GetRui( file.storeHeader ), "isNew", storeIsNew )
+ ComboButton_SetNew( file.storeButton, storeIsNew )
+ }
+
+ // "Callsign"
+ {
+ bool anyNewBanners = HasAnyNewCallsignBanners( player )
+ bool anyNewPatches = HasAnyNewCallsignPatches( player )
+ bool anyNewFactions = HasAnyNewFactions( player )
+ bool anyNewCallsignHeader = (anyNewBanners || anyNewPatches || anyNewFactions)
+
+ RuiSetBool( Hud_GetRui( file.callsignHeader ), "isNew", anyNewCallsignHeader )
+ ComboButton_SetNew( file.bannerButton, anyNewBanners )
+ ComboButton_SetNew( file.patchButton, anyNewPatches )
+ ComboButton_SetNew( file.factionButton, anyNewFactions )
+ }
+ }
+}
+
+void function LobbyMenuUpdate( var menu )
+{
+ EndSignal( uiGlobal.signalDummy, "CleanupInGameMenus" )
+
+ while ( GetTopNonDialogMenu() == menu )
+ {
+ WaitFrame()
+ }
+}
+
+
+
+void function OnLobbyMenu_Close()
+{
+ Signal( uiGlobal.signalDummy, "OnCloseLobbyMenu" )
+}
+
+void function OnLobbyMenu_NavigateBack()
+{
+ LeaveDialog()
+}
+
+function GameStartTime_Changed()
+{
+ printt( "GameStartTime_Changed", level.ui.gameStartTime )
+ UpdateGameStartTimeCounter()
+}
+
+function UpdateGameStartTimeCounter()
+{
+ if ( level.ui.gameStartTime == null )
+ {
+ MatchmakingSetSearchText( "" )
+ MatchmakingSetCountdownTimer( 0.0 )
+ HideMatchmakingStatusIcons()
+ return
+ }
+
+ MatchmakingSetSearchText( "#STARTING_IN_LOBBY" )
+ MatchmakingSetCountdownTimer( expect float( level.ui.gameStartTime + 0.0 ) )
+ ShowMatchmakingStatusIcons()
+}
+
+function UpdateDebugStatus()
+{
+ EndSignal( uiGlobal.signalDummy, "CleanupInGameMenus" )
+
+ OnThreadEnd(
+ function() : ()
+ {
+ foreach ( elem in file.MMDevStringElems )
+ Hud_Hide( elem )
+ }
+ )
+
+ foreach ( elem in file.MMDevStringElems )
+ Hud_Show( elem )
+
+ while ( true )
+ {
+ local strstr = GetLobbyDevString()
+ foreach ( elem in file.MMDevStringElems )
+ Hud_SetText( elem, strstr )
+
+ WaitFrameOrUntilLevelLoaded()
+ }
+}
+
+void function SetMapInfo( string mapName )
+{
+ var nextMapImage = Hud_GetChild( file.menu, "NextMapImage" )
+
+ asset mapImage = GetMapImageForMapName( mapName )
+ RuiSetImage( Hud_GetRui( nextMapImage ), "basicImage", mapImage )
+ Hud_Show( nextMapImage )
+
+ Hud_SetText( file.nextMapNameLabel, GetMapDisplayName( mapName ) )
+}
+
+void function SetModeInfo( string modeName )
+{
+ var nextModeIcon = Hud_GetChild( file.menu, "NextModeIcon" )
+ RuiSetImage( Hud_GetRui( nextModeIcon ), "basicImage", GetPlaylistThumbnailImage( modeName ) )
+ Hud_Show( nextModeIcon )
+
+ Hud_SetText( file.nextGameModeLabel, GetGameModeDisplayName( modeName ) )
+}
+
+function Privatematch_map_Changed()
+{
+ if ( !IsPrivateMatch() )
+ return
+ if ( !IsLobby() )
+ return
+
+ string mapName = PrivateMatch_GetSelectedMap()
+ SetMapInfo( mapName )
+}
+
+function Privatematch_mode_Changed()
+{
+ if ( !IsPrivateMatch() )
+ return
+ if ( !IsLobby() )
+ return
+
+ string modeName = PrivateMatch_GetSelectedMode()
+ SetModeInfo( modeName )
+
+ UpdatePrivateMatchButtons()
+ UpdateMatchSettingsForGamemode()
+}
+
+
+function Privatematch_starting_Changed()
+{
+ if ( !IsPrivateMatch() )
+ return
+ if ( !IsLobby() )
+ return
+
+ UpdatePrivateMatchButtons()
+ UpdateFooterOptions()
+}
+
+
+function UpdatePrivateMatchButtons()
+{
+ var menu = file.menu
+
+ if ( level.ui.privatematch_starting == ePrivateMatchStartState.STARTING )
+ {
+ RHud_SetText( file.startMatchButton, "#STOP_MATCH" )
+ Hud_SetLocked( file.selectMapButton, true )
+ Hud_SetLocked( file.selectModeButton, true )
+ Hud_SetLocked( file.matchSettingsButton, true )
+ Hud_SetLocked( file.inviteFriendsButton, true )
+ }
+ else
+ {
+ RHud_SetText( file.startMatchButton, "#START_MATCH" )
+ Hud_SetLocked( file.selectMapButton, false )
+ Hud_SetLocked( file.selectModeButton, false )
+ Hud_SetLocked( file.inviteFriendsButton, false )
+
+ string modeName = PrivateMatch_GetSelectedMode()
+ bool settingsLocked = IsFDMode( modeName )
+
+ if ( settingsLocked && uiGlobal.activeMenu == GetMenu( "MatchSettingsMenu" ) )
+ CloseActiveMenu()
+
+ Hud_SetLocked( file.matchSettingsButton, settingsLocked )
+ }
+}
+
+function UpdateLobbyUI()
+{
+ if ( uiGlobal.updatingLobbyUI )
+ return
+ uiGlobal.updatingLobbyUI = true
+
+ thread UpdateLobby()
+ thread UpdateDebugStatus()
+ thread UpdatePlayerInfo()
+
+ if ( uiGlobal.EOGOpenInLobby )
+ EOGOpen()
+
+ WaitSignal( uiGlobal.signalDummy, "CleanupInGameMenus" )
+ uiGlobal.updatingLobbyUI = false
+}
+
+function UpdateLobby()
+{
+ EndSignal( uiGlobal.signalDummy, "CleanupInGameMenus" )
+
+ var menu = file.menu
+
+ WaitFrameOrUntilLevelLoaded()
+
+ while ( true )
+ {
+ if ( !IsConnected() )
+ {
+ WaitFrameOrUntilLevelLoaded()
+ continue
+ }
+
+ menu.RunAnimationScript( "PrivateMatchLobby" )
+ // Force the animation scripts (which have zero duration) to complete before anything can cancel them.
+ ForceUpdateHUDAnimations()
+
+ UpdatePrivateMatchButtons()
+
+ int gamemodeIdx = expect int( level.ui.privatematch_mode )
+ int numPlaylistOverrides = GetPlaylistVarOverridesCount()
+ string playlistOverridesDesc = ""
+ for ( int varIdx = 0; varIdx < numPlaylistOverrides; ++varIdx )
+ {
+ // temp fix for playlistoverrides that aren't handled by private match
+ string varName = GetPlaylistVarOverrideNameByIndex( varIdx )
+
+ if ( varName in MatchSettings_PlaylistVarLabels )
+ {
+ float varOrigVal = float( GetCurrentPlaylistGamemodeByIndexVar( gamemodeIdx, varName, false ) )
+ float varOverrideVal = float( GetCurrentPlaylistGamemodeByIndexVar( gamemodeIdx, varName, true ) )
+ if ( varOrigVal == varOverrideVal && !IsNorthstarServer() ) // stuff seems to break outside of northstar servers since we dont always use private_match playlist
+ continue
+
+ string label = Localize( MatchSettings_PlaylistVarLabels[varName] ) + ": "
+ string value = MatchSettings_FormatPlaylistVarValue( varName, varOverrideVal )
+ playlistOverridesDesc = playlistOverridesDesc + label + "`2" + value + " `0\n"
+ }
+ else
+ {
+ foreach ( string category in GetPrivateMatchSettingCategories() )
+ {
+ foreach ( CustomMatchSettingContainer setting in GetPrivateMatchCustomSettingsForCategory( category ) )
+ {
+ if ( setting.playlistVar == varName )
+ {
+ if ( setting.isEnumSetting )
+ {
+ playlistOverridesDesc += Localize( setting.localizedName ) + ": `2" + setting.enumNames[ setting.enumValues.find( expect string ( GetCurrentPlaylistVar( varName ) ) ) ] + "`0\n"
+ }
+ else
+ playlistOverridesDesc += Localize( setting.localizedName ) + ": `2" + GetCurrentPlaylistVar( varName ) + "`0\n"
+
+ break
+ }
+ }
+ }
+
+ // sorta temp: ideally wanna localise playlist var names in the future
+ }
+ }
+
+ if ( playlistOverridesDesc.len() )
+ {
+ RuiSetString( Hud_GetRui( file.matchSettingsPanel ), "description", playlistOverridesDesc )
+ Hud_Show( file.matchSettingsPanel )
+ }
+ else
+ {
+ Hud_Hide( file.matchSettingsPanel )
+ }
+
+ if ( GetUIPlayer() && GetPersistentVar( "privateMatchState" ) == 1 )
+ Hud_SetVisible( file.spectatorLabel, true )
+ else
+ Hud_SetVisible( file.spectatorLabel, false )
+
+ WaitFrameOrUntilLevelLoaded()
+ }
+}
+
+void function OnSettingsButton_Activate( var button )
+{
+ if ( level.ui.privatematch_starting == ePrivateMatchStartState.STARTING )
+ return
+
+ AdvanceMenu( GetMenu( "MatchSettingsMenu" ) )
+}
+
+
+void function OnPrivateMatchButton_Activate( var button )
+{
+ ShowPrivateMatchConnectDialog()
+ ClientCommand( "match_playlist private_match" )
+ ClientCommand( "StartPrivateMatchSearch" )
+}
+
+void function OnStartMatchButton_Activate( var button )
+{
+ if ( AmIPartyLeader() || GetPartySize() == 1 )
+ ClientCommand( "PrivateMatchLaunch" )
+}
+
+function HandleLockedCustomMenuItem( menu, button, tipInfo, hideTip = false )
+{
+ array<var> elements = GetElementsByClassname( menu, "HideWhenLocked" )
+ var buttonTooltip = Hud_GetChild( menu, "ButtonTooltip" )
+ var toolTipLabel = Hud_GetChild( buttonTooltip, "Label" )
+
+ if ( Hud_IsLocked( button ) && !hideTip )
+ {
+ foreach( elem in elements )
+ Hud_Hide( elem )
+
+ local tipArray = clone tipInfo
+ tipInfo.resize( 6, null )
+
+ Hud_SetText( toolTipLabel, tipInfo[0], tipInfo[1], tipInfo[2], tipInfo[3], tipInfo[4], tipInfo[5] )
+
+ local buttonPos = button.GetAbsPos()
+ local buttonHeight = button.GetHeight()
+ local tooltipHeight = buttonTooltip.GetHeight()
+ local yOffset = ( tooltipHeight - buttonHeight ) / 2.0
+
+ buttonTooltip.SetPos( buttonPos[0] + button.GetWidth() * 0.9, buttonPos[1] - yOffset )
+ Hud_Show( buttonTooltip )
+
+ return true
+ }
+ else
+ {
+ foreach( elem in elements )
+ Hud_Show( elem )
+ Hud_Hide( buttonTooltip )
+ }
+ return false
+}
+
+void function PrivateMatchSwitchTeams( button )
+{
+ if ( !IsPrivateMatch() )
+ return
+
+ if ( !IsConnected() )
+ return
+
+ if ( uiGlobal.activeMenu != file.menu )
+ return
+
+ EmitUISound( "Menu_GameSummary_ScreenSlideIn" )
+
+ ClientCommand( "PrivateMatchSwitchTeams" )
+}
+
+void function HideMatchmakingStatusIcons()
+{
+ foreach ( element in file.matchStatusRuis )
+ RuiSetBool( Hud_GetRui( element ), "iconVisible", false )
+}
+
+void function ShowMatchmakingStatusIcons()
+{
+ foreach ( element in file.matchStatusRuis )
+ RuiSetBool( Hud_GetRui( element ), "iconVisible", true )
+}
+
+void function MatchmakingSetSearchText( string searchText, var param1 = "", var param2 = "", var param3 = "", var param4 = "" )
+{
+ foreach ( element in file.matchStatusRuis )
+ {
+ RuiSetBool( Hud_GetRui( element ), "statusHasText", searchText != "" )
+
+ RuiSetString( Hud_GetRui( element ), "statusText", Localize( searchText, param1, param2, param3, param4 ) )
+ }
+}
+
+
+void function MatchmakingSetCountdownTimer( float time )
+{
+ foreach ( element in file.matchStatusRuis )
+ {
+ RuiSetBool( Hud_GetRui( element ), "timerHasText", time != 0.0 )
+ RuiSetGameTime( Hud_GetRui( element ), "timerEndTime", time )
+ }
+}
+
+
+void function OnPrivateLobbyLevelInit()
+{
+ UpdateCallsignElement( file.callsignCard )
+ RefreshCreditsAvailable()
+}
+
+
+function UpdatePlayerInfo()
+{
+ EndSignal( uiGlobal.signalDummy, "CleanupInGameMenus" )
+
+ var menu = file.menu
+
+ WaitFrameOrUntilLevelLoaded()
+
+ while ( true )
+ {
+ RefreshCreditsAvailable()
+ WaitFrame()
+ }
+}
+
+void function OnPrivateMatchMenu_Open()
+{
+ Lobby_SetFDMode( false )
+ OnLobbyMenu_Open()
+} \ No newline at end of file
diff --git a/Northstar.Client/mod/scripts/vscripts/ui/panel_mainmenu.nut b/Northstar.Client/mod/scripts/vscripts/ui/panel_mainmenu.nut
new file mode 100644
index 00000000..acd11237
--- /dev/null
+++ b/Northstar.Client/mod/scripts/vscripts/ui/panel_mainmenu.nut
@@ -0,0 +1,844 @@
+untyped
+
+global function InitMainMenuPanel
+global function UpdatePromoData
+
+global function UICodeCallback_GetOnPartyServer
+global function UICodeCallback_MainMenuPromosUpdated
+
+struct
+{
+ var menu
+ var panel
+ array<var> spButtons
+ array<void functionref()> spButtonFuncs
+ var mpButton
+ var fdButton
+ void functionref() mpButtonActivateFunc = null
+ var buttonData
+ array<var> menuButtons
+ var activeProfile
+ var serviceStatus
+
+ MainMenuPromos& promoData
+ var whatsNew
+ var spotlightPanel
+ array<var> spotlightButtons
+
+ bool installing = false
+} file
+
+const DEBUG_PERMISSIONS = false
+
+void function InitMainMenuPanel()
+{
+ RegisterSignal( "EndShowMainMenuPanel" )
+
+ file.panel = GetPanel( "MainMenuPanel" )
+ file.menu = GetParentMenu( file.panel )
+
+ AddPanelEventHandler( file.panel, eUIEvent.PANEL_SHOW, OnShowMainMenuPanel )
+ AddPanelEventHandler( file.panel, eUIEvent.PANEL_HIDE, OnHideMainMenuPanel )
+
+ file.menuButtons = GetElementsByClassname( file.menu, "MainMenuButtonClass" )
+ AddEventHandlerToButtonClass( file.menu, "MainMenuButtonClass", UIE_CLICK, MainMenuButton_Activate )
+
+ file.spotlightPanel = Hud_GetChild( file.panel, "SpotlightPanel" )
+ file.spotlightButtons = GetElementsByClassname( file.menu, "SpotlightButtonClass" )
+ foreach ( button in file.spotlightButtons )
+ button.s.link <- ""
+ AddEventHandlerToButtonClass( file.menu, "SpotlightButtonClass", UIE_CLICK, SpotlightButton_Activate )
+
+ file.activeProfile = Hud_GetChild( file.panel, "ActiveProfile" )
+ file.serviceStatus = Hud_GetRui( Hud_GetChild( file.panel, "ServiceStatus" ) )
+ file.whatsNew = Hud_GetRui( Hud_GetChild( file.panel, "WhatsNew" ) )
+
+ ComboStruct comboStruct = ComboButtons_Create( file.panel )
+
+ int headerIndex = 0
+ int buttonIndex = 0
+ var campaignHeader = AddComboButtonHeader( comboStruct, headerIndex, "#GAMEMODE_SOLO" )
+ file.spButtons.append( AddComboButton( comboStruct, headerIndex, buttonIndex, "" ) )
+ file.spButtonFuncs.append( DoNothing() )
+ Hud_AddEventHandler( file.spButtons[buttonIndex], UIE_CLICK, RunSPButton0 )
+ buttonIndex++
+ file.spButtons.append( AddComboButton( comboStruct, headerIndex, buttonIndex, "" ) )
+ file.spButtonFuncs.append( DoNothing() )
+ Hud_AddEventHandler( file.spButtons[buttonIndex], UIE_CLICK, RunSPButton1 )
+ buttonIndex++
+ file.spButtons.append( AddComboButton( comboStruct, headerIndex, buttonIndex, "" ) )
+ file.spButtonFuncs.append( DoNothing() )
+ Hud_AddEventHandler( file.spButtons[buttonIndex], UIE_CLICK, RunSPButton2 )
+ buttonIndex++
+ UpdateSPButtons()
+
+ headerIndex++
+ buttonIndex = 0
+ var multiplayerHeader = AddComboButtonHeader( comboStruct, headerIndex, "#MULTIPLAYER_ALLCAPS" )
+ file.mpButton = AddComboButton( comboStruct, headerIndex, buttonIndex++, "#MULTIPLAYER_LAUNCH" )
+ Hud_AddEventHandler( file.mpButton, UIE_CLICK, OnPlayMPButton_Activate )
+ file.fdButton = AddComboButton( comboStruct, headerIndex, buttonIndex++, "#GAMEMODE_COOP" )
+ Hud_AddEventHandler( file.fdButton, UIE_CLICK, OnPlayFDButton_Activate )
+
+ headerIndex++
+ buttonIndex = 0
+ var settingsHeader = AddComboButtonHeader( comboStruct, headerIndex, "#MENU_HEADER_SETTINGS" )
+ var controlsButton = AddComboButton( comboStruct, headerIndex, buttonIndex++, "#CONTROLS" )
+ Hud_AddEventHandler( controlsButton, UIE_CLICK, ActivateControlsMenu )
+ #if CONSOLE_PROG
+ var avButton = AddComboButton( comboStruct, headerIndex, buttonIndex++, "#AUDIO_VIDEO" )
+ Hud_AddEventHandler( avButton, UIE_CLICK, ActivateAudioVisualMenu )
+ #elseif PC_PROG
+ var audioButton = AddComboButton( comboStruct, headerIndex, buttonIndex++, "#AUDIO" )
+ Hud_AddEventHandler( audioButton, UIE_CLICK, AdvanceMenuEventHandler( GetMenu( "AudioMenu" ) ) )
+ var videoButton = AddComboButton( comboStruct, headerIndex, buttonIndex++, "#VIDEO" )
+ Hud_AddEventHandler( videoButton, UIE_CLICK, AdvanceMenuEventHandler( GetMenu( "VideoMenu" ) ) )
+ #endif
+
+ var spotlightLargeButton = Hud_GetChild( file.spotlightPanel, "SpotlightLarge" )
+ spotlightLargeButton.SetNavLeft( file.spButtons[0] )
+
+ var spotlightSmall0Button = Hud_GetChild( file.spotlightPanel, "SpotlightSmall0" )
+ spotlightSmall0Button.SetNavLeft( file.spButtons[0] )
+
+ file.buttonData = []
+
+ #if PC_PROG
+ file.buttonData.append( { name = "#QUIT", activateFunc = OnQuitButton_Activate } )
+ #endif // PC_PROG
+
+ if ( file.buttonData.len() )
+ {
+ comboStruct.navUpButton = file.menuButtons[ expect int( file.buttonData.len() ) - 1 ]
+ comboStruct.navDownButton = file.menuButtons[0]
+
+ foreach ( button in file.menuButtons )
+ button.SetNavRight( spotlightLargeButton )
+ }
+
+ comboStruct.navRightButton = spotlightLargeButton
+
+ ComboButtons_Finalize( comboStruct )
+
+ //AddPanelFooterOption( file.panel, BUTTON_A, "#A_BUTTON_SELECT" )
+ //AddPanelFooterOption( file.panel, BUTTON_B, "#B_BUTTON_CLOSE", "#CLOSE" )
+ //AddPanelFooterOption( file.panel, BUTTON_BACK, "", "", ClosePostGameMenu )
+
+ thread TrackInstallProgress()
+}
+
+void function OnShowMainMenuPanel()
+{
+ Signal( uiGlobal.signalDummy, "EndShowMainMenuPanel" )
+ EndSignal( uiGlobal.signalDummy, "EndShowMainMenuPanel" )
+
+ foreach ( button in file.menuButtons )
+ {
+ int buttonID = int( Hud_GetScriptID( button ) )
+
+ if ( buttonID < file.buttonData.len() )
+ {
+ if ( "updateFunc" in file.buttonData[buttonID] )
+ file.buttonData[buttonID].updateFunc.call( this, button )
+ else
+ Hud_SetEnabled( button, true )
+
+ RuiSetString( Hud_GetRui( button ), "buttonText", file.buttonData[buttonID].name )
+ Hud_Show( button )
+ }
+ else
+ {
+ Hud_Hide( button )
+ }
+ }
+
+ #if PS4_PROG
+ thread EnableCheckPlus()
+ #endif // PS4_PROG
+
+ UpdateSPButtons()
+ thread UpdatePlayButton( file.mpButton )
+ thread MonitorTrialVersionChange()
+
+ #if DURANGO_PROG
+ SetLabelRuiText( file.activeProfile, Durango_GetGameDisplayName() )
+ Hud_Show( file.activeProfile )
+ #endif // DURANGO_PROG
+
+ ExecCurrentGamepadButtonConfig()
+ ExecCurrentGamepadStickConfig()
+
+
+ string defaultButtonRowFocus = "ButtonRow0x0"
+ bool shouldFocusMultiplayer = GetMenuWasMultiplayerPlayedLast()
+ if ( shouldFocusMultiplayer )
+ defaultButtonRowFocus = "ButtonRow1x0"
+
+ SetPanelDefaultFocus( file.panel, Hud_GetChild( file.panel, defaultButtonRowFocus ) )
+ PanelFocusDefault( file.panel )
+}
+
+void function EnableCheckPlus()
+{
+ WaitFrame() // ???: doesn't work without a wait
+ if ( !Ps4_CheckPlus_Allowed() && !IsSingleplayer() )
+ {
+ printt( "scheduling plus check" )
+ Ps4_CheckPlus_Schedule()
+ }
+}
+
+void function OnHideMainMenuPanel()
+{
+ Signal( uiGlobal.signalDummy, "EndShowMainMenuPanel" )
+}
+
+void function UpdatePlayButton( var button )
+{
+ #if CONSOLE_PROG
+ bool isOnline
+ bool hasPermission
+ bool isFullyInstalled
+ #endif
+
+ #if DURANGO_PROG
+ bool isGuest
+ #elseif PS4_PROG
+ bool isPSNConnected
+ bool isOverAge
+ bool hasPlus
+ bool hasLatestPatch
+ #elseif PC_PROG
+ bool isOriginConnected
+ bool hasLatestPatch
+ #endif
+
+ bool isStryderAuthenticated
+ bool isMPAllowed
+ bool isLocked
+ string buttonText
+ string message
+ bool isMessageVisible
+
+ while ( GetTopNonDialogMenu() == file.menu )
+ {
+ bool isSpotlightReady = file.promoData.version != 0 ? true : false
+ Hud_SetVisible( file.spotlightPanel, isSpotlightReady )
+
+ if ( !Hud_IsFocused( button ) )
+ {
+ RuiSetBool( file.serviceStatus, "isVisible", false )
+ WaitFrame()
+ continue
+ }
+
+ #if DURANGO_PROG
+ isFullyInstalled = IsGameFullyInstalled()
+ isOnline = Console_IsOnline()
+ isGuest = Durango_IsGuest()
+ hasPermission = Console_HasPermissionToPlayMultiplayer()
+ isStryderAuthenticated = IsStryderAuthenticated()
+ isMPAllowed = IsStryderAllowingMP()
+
+ if ( DEBUG_PERMISSIONS )
+ {
+ printt( "isFullyInstalled:", isFullyInstalled )
+ printt( "isOnline:", isOnline )
+ printt( "isGuest:", isGuest )
+ printt( "hasPermission:", hasPermission )
+ printt( "isStryderAuthenticated:", isStryderAuthenticated )
+ printt( "isMPAllowedByStryder:", isMPAllowed )
+ }
+
+ buttonText = "#MULTIPLAYER_LAUNCH"
+ message = ""
+
+ if ( !isOnline )
+ {
+ message = "#INTERNET_NOT_FOUND"
+ file.mpButtonActivateFunc = null
+ }
+ else if ( isGuest )
+ {
+ buttonText = "#SWITCH_PROFILE"
+ message = "#GUESTS_NOT_SUPPORTED"
+ file.mpButtonActivateFunc = XB1_SwitchAccount
+ }
+ else if ( !hasPermission || !isMPAllowed )
+ {
+ message = "#MULTIPLAYER_NOT_AVAILABLE"
+ file.mpButtonActivateFunc = null
+ }
+ else if ( !isStryderAuthenticated )
+ {
+ message = "#CONTACTING_RESPAWN_SERVERS"
+ file.mpButtonActivateFunc = null
+ }
+ else if ( !isFullyInstalled )
+ {
+ //message = "#INSTALL_IN_PROGRESS"
+ file.mpButtonActivateFunc = LaunchMP
+ }
+ else
+ {
+ file.mpButtonActivateFunc = LaunchMP
+ }
+
+ isLocked = file.mpButtonActivateFunc == null ? true : false
+ Hud_SetLocked( button, isLocked )
+
+ #elseif PS4_PROG
+
+ isFullyInstalled = IsGameFullyInstalled()
+ hasLatestPatch = HasLatestPatch()
+ isOnline = Console_IsOnline()
+ isPSNConnected = Ps4_PSN_Is_Loggedin()
+ hasPermission = Console_HasPermissionToPlayMultiplayer()
+ isOverAge = !PS4_is_NetworkStatusAgeRestriction()
+ hasPlus = Ps4_CheckPlus_Allowed()
+ isStryderAuthenticated = IsStryderAuthenticated()
+ isMPAllowed = IsStryderAllowingMP()
+
+ if ( DEBUG_PERMISSIONS )
+ {
+ printt( "isFullyInstalled:", isFullyInstalled )
+ printt( "hasLatestPatch:", hasLatestPatch )
+ printt( "isOnline:", isOnline )
+ printt( "isPSNConnected:", isPSNConnected )
+ printt( "hasPermission:", hasPermission )
+ printt( "isOverAge:", isOverAge )
+ printt( "hasPlus:", hasPlus )
+ printt( "isStryderAuthenticated:", isStryderAuthenticated )
+ printt( "isMPAllowedByStryder:", isMPAllowed )
+ }
+
+ buttonText = "#MULTIPLAYER_LAUNCH"
+ message = ""
+
+ if ( !isOnline )
+ {
+ message = "#INTERNET_NOT_FOUND"
+ file.mpButtonActivateFunc = null
+ }
+ else if ( PS4_getUserNetworkingResolution() == PS4_NETWORK_STATUS_UNKNOWN )
+ {
+ message = "#INTERNET_NOT_FOUND"
+ file.mpButtonActivateFunc = LaunchMP
+ }
+ else if ( !hasLatestPatch )
+ {
+ message = "#UPDATE_AVAILABLE"
+ file.mpButtonActivateFunc = null
+ }
+ else if ( PS4_getUserNetworkingResolution() == PS4_NETWORK_STATUS_IN_ERROR )
+ {
+ message = "#PSN_HAD_ERROR"
+ file.mpButtonActivateFunc = LaunchMP
+ }
+ else if ( !isPSNConnected )
+ {
+ buttonText = "#PS4_SIGN_IN"
+ message = "#PS4_DISCONNECT_NOT_SIGNED_IN_TO_PSN"
+ file.mpButtonActivateFunc = PS4_PSNSignIn
+ }
+ else if ( !isFullyInstalled )
+ {
+ //message = "#INSTALL_IN_PROGRESS"
+ file.mpButtonActivateFunc = null
+ }
+ else if ( !isOverAge )
+ {
+ message = "#MULTIPLAYER_AGE_RESTRICTED"
+ file.mpButtonActivateFunc = null
+ }
+ else if ( !hasPermission || !isMPAllowed ) // A more general permission check. Can fail if not patched, underage profile logged in to another controller, network issue, etc.
+ {
+ message = "#MULTIPLAYER_NOT_AVAILABLE"
+ file.mpButtonActivateFunc = null
+ }
+ else if ( !hasPlus )
+ {
+ //buttonText = "#PS4_GET_PLAYSTATION_PLUS"
+ //message = "#PSN_MUST_BE_PLUS_USER"
+ //file.mpButtonActivateFunc = PS4_PlusSignUp
+ // Their is a race on this. The function may not be completed.
+
+
+ // The LaunchMP handles this race and will retry/ issue an error dialog if needed.
+ file.mpButtonActivateFunc = LaunchMP
+ }
+ else if ( !isStryderAuthenticated )
+ {
+ message = "#CONTACTING_RESPAWN_SERVERS"
+ file.mpButtonActivateFunc = null
+ }
+ else
+ {
+ file.mpButtonActivateFunc = LaunchMP
+ }
+
+ isLocked = file.mpButtonActivateFunc == null ? true : false
+ Hud_SetLocked( button, isLocked )
+
+ #elseif PC_PROG
+
+ hasLatestPatch = Origin_IsUpToDate()
+ isOriginConnected = Origin_IsEnabled() ? Origin_IsOnline() : true
+ isStryderAuthenticated = IsStryderAuthenticated()
+ isMPAllowed = IsStryderAllowingMP()
+
+ if ( DEBUG_PERMISSIONS )
+ {
+ printt( "isOriginConnected:", isOriginConnected )
+ printt( "isStryderAuthenticated:", isStryderAuthenticated )
+ }
+
+ buttonText = "#MULTIPLAYER_LAUNCH"
+ message = ""
+
+ if ( !isOriginConnected )
+ {
+ message = "#ORIGIN_IS_OFFLINE"
+ file.mpButtonActivateFunc = null
+ }
+ else if ( !isStryderAuthenticated )
+ {
+ message = "#CONTACTING_RESPAWN_SERVERS"
+ file.mpButtonActivateFunc = null
+ }
+ else if ( !isMPAllowed )
+ {
+ message = "#MULTIPLAYER_NOT_AVAILABLE"
+ file.mpButtonActivateFunc = null
+ }
+ else if ( !hasLatestPatch )
+ {
+ message = "#ORIGIN_UPDATE_AVAILABLE"
+ file.mpButtonActivateFunc = null
+ }
+ else
+ {
+ file.mpButtonActivateFunc = LaunchMP
+ }
+
+ isLocked = file.mpButtonActivateFunc == null ? true : false
+ Hud_SetLocked( button, isLocked )
+ #endif
+
+ if ( Script_IsRunningTrialVersion() && !IsTrialPeriodActive() && file.mpButtonActivateFunc != LaunchGamePurchase )
+ {
+ buttonText = "#MENU_GET_THE_FULL_GAME"
+ file.mpButtonActivateFunc = LaunchGamePurchase
+ Hud_SetLocked( button, false )
+ message = ""
+ }
+
+ ComboButton_SetText( file.mpButton, buttonText )
+
+ //if ( Hud_IsLocked( button ) || buttonText == "#MENU_GET_THE_FULL_GAME" )
+ //{
+ // ComboButton_SetText( file.fdButton, "" )
+ // Hud_SetEnabled( file.fdButton, false )
+ //}
+ //else
+ //{
+ //ComboButton_SetText( file.fdButton, "#MULTIPLAYER_LAUNCH_FD" )
+ ComboButton_SetText( file.fdButton, "Launch Northstar" ) // this needs to use localised text at some point when we have a modular way of doing that
+ Hud_SetEnabled( file.fdButton, true )
+ //}
+
+ if ( file.installing )
+ message = ""
+ else if ( message == "" )
+ message = GetConVarString( "rspn_motd" )
+
+ RuiSetString( file.serviceStatus, "messageText", message )
+
+ isMessageVisible = message != "" ? true : false
+ RuiSetBool( file.serviceStatus, "isVisible", isMessageVisible )
+
+ WaitFrame()
+ //wait 2
+ }
+}
+
+void function XB1_SwitchAccount()
+{
+ Durango_ShowAccountPicker()
+}
+
+void function PS4_PSNSignIn()
+{
+ Ps4_LoginDialog_Schedule()
+}
+
+void function PS4_PlusSignUp()
+{
+ if ( Ps4_ScreenPlusDialog_Schedule() )
+ {
+ while ( Ps4_ScreenPlusDialog_Running() )
+ WaitFrame()
+
+ if ( Ps4_ScreenPlusDialog_Allowed() )
+ Ps4_CheckPlus_Schedule()
+ }
+}
+
+void function MainMenuButton_Activate( var button )
+{
+ int buttonID = int( Hud_GetScriptID( button ) )
+
+ Assert( file.buttonData )
+
+ if ( file.buttonData[buttonID].activateFunc )
+ file.buttonData[buttonID].activateFunc.call( this )
+}
+
+void function OnPlayFDButton_Activate( var button ) // repurposed for launching northstar lobby
+{
+ //if ( file.mpButtonActivateFunc == null )
+ // printt( "file.mpButtonActivateFunc is null" )
+
+ if ( !Hud_IsLocked( button ) )// && file.mpButtonActivateFunc != null )
+ {
+ //Lobby_SetAutoFDOpen( true )
+ //// Lobby_SetFDMode( true )
+ //thread file.mpButtonActivateFunc()
+
+ //ClientCommand( "setplaylist tdm" )
+ //ClientCommand( "map mp_lobby" )
+
+ NSTryAuthWithLocalServer()
+ thread TryAuthWithLocalServer()
+ }
+}
+
+void function TryAuthWithLocalServer()
+{
+ while ( NSIsAuthenticatingWithServer() )
+ WaitFrame()
+
+ if ( NSWasAuthSuccessful() )
+ NSCompleteAuthWithLocalServer()
+
+ ClientCommand( "setplaylist tdm" )
+ ClientCommand( "map mp_lobby" )
+}
+
+void function OnPlayMPButton_Activate( var button )
+{
+ if ( file.mpButtonActivateFunc == null )
+ printt( "file.mpButtonActivateFunc is null" )
+
+ if ( !Hud_IsLocked( button ) && file.mpButtonActivateFunc != null )
+ {
+ Lobby_SetAutoFDOpen( false )
+ // Lobby_SetFDMode( false )
+ thread file.mpButtonActivateFunc()
+ }
+}
+
+void function UICodeCallback_GetOnPartyServer()
+{
+ uiGlobal.launching = eLaunching.MULTIPLAYER_INVITE
+ Lobby_SetAutoFDOpen( false )
+ // Lobby_SetFDMode( false )
+ LaunchGame()
+}
+
+#if PC_PROG
+void function OnQuitButton_Activate()
+{
+ DialogData dialogData
+ dialogData.header = "#MENU_QUIT_GAME_CONFIRM"
+
+ AddDialogButton( dialogData, "#CANCEL" )
+ AddDialogButton( dialogData, "#QUIT", Quit )
+
+ AddDialogFooter( dialogData, "#A_BUTTON_SELECT" )
+ AddDialogFooter( dialogData, "#B_BUTTON_CANCEL" )
+
+ OpenDialog( dialogData )
+}
+
+void function Quit()
+{
+ ClientCommand( "quit" )
+}
+#endif // #if PC_PROG
+
+void function MonitorTrialVersionChange()
+{
+ bool isTrialVersion
+ bool lastIsTrialVersion = Script_IsRunningTrialVersion()
+
+ while ( GetTopNonDialogMenu() == file.menu )
+ {
+ isTrialVersion = Script_IsRunningTrialVersion()
+
+ if ( isTrialVersion != lastIsTrialVersion )
+ UpdateSPButtons()
+
+ lastIsTrialVersion = isTrialVersion
+
+ WaitFrame()
+ }
+}
+
+void function UpdateSPButtons()
+{
+ foreach( button in file.spButtons )
+ {
+ ComboButton_SetText( button, "" )
+ Hud_SetEnabled( button, false )
+ }
+
+ int buttonIndex = 0
+
+ if ( Script_IsRunningTrialVersion() )
+ {
+ if ( HasStartedGameEver() )
+ AddSPButton( buttonIndex, TrainingModeSelect, "#SP_TRIAL_MENU_TRAINING" )
+ else
+ AddSPButton( buttonIndex, LaunchSPNew, "#SP_TRIAL_MENU_TRAINING" )
+ buttonIndex++
+
+ if ( HasStartedGameEver() )
+ {
+ AddSPButton( buttonIndex, TrialMissionSelect, "#SP_TRIAL_MENU_MISSION" )
+ buttonIndex++
+ }
+
+ AddSPButton( buttonIndex, LaunchGamePurchase, "#MENU_GET_THE_FULL_GAME" )
+ buttonIndex++
+ }
+ else
+ {
+ if ( HasValidSaveGame() )
+ {
+ AddSPButton( buttonIndex, LaunchSPContinue, "#MENU_CONTINUE_GAME" )
+ buttonIndex++
+ }
+
+ if ( HasStartedGameEver() )
+ {
+ AddSPButton( buttonIndex, LaunchSPMissionSelect, "#MENU_MISSION_SELECT" )
+ buttonIndex++
+ }
+
+ AddSPButton( buttonIndex, LaunchSPNew, "#MENU_NEW_GAME" )
+ }
+}
+
+void function AddSPButton( int index, void functionref() func, string text )
+{
+ var button = file.spButtons[ index ]
+ ComboButton_SetText( button, text )
+ Hud_SetEnabled( button, true )
+ file.spButtonFuncs[ index ] = func
+}
+
+void function DoNothing()
+{
+}
+
+void function RunSPButton0( var button )
+{
+ void functionref() func = file.spButtonFuncs[ 0 ]
+ func()
+}
+
+void function RunSPButton1( var button )
+{
+ void functionref() func = file.spButtonFuncs[ 1 ]
+ func()
+}
+
+void function RunSPButton2( var button )
+{
+ void functionref() func = file.spButtonFuncs[ 2 ]
+ func()
+}
+
+void function ActivateControlsMenu( var button )
+{
+ #if CONSOLE_PROG
+ if ( GetEULAVersionAccepted() < 1 ) // Treat as binary for now, as discussed with Preston.
+ {
+ if ( uiGlobal.activeMenu == GetMenu( "EULADialog" ) )
+ return
+
+ if ( IsDialog( uiGlobal.activeMenu ) )
+ CloseActiveMenu( true )
+
+ uiGlobal.consoleSettingMenu = eConsoleSettingsMenu.CONTROLS_MENU
+
+ EULA_Dialog()
+ return
+ }
+ else
+ {
+ AdvanceMenu( GetMenu( "ControlsMenu" ) )
+ return
+ }
+ #endif
+
+ #if PC_PROG
+ AdvanceMenu( GetMenu( "ControlsMenu" ) )
+ #endif
+}
+
+void function ActivateAudioVisualMenu( var button ) //This is only run on console
+{
+ if ( GetEULAVersionAccepted() < 1 ) // Treat as binary for now, as discussed with Preston.
+ {
+ if ( uiGlobal.activeMenu == GetMenu( "EULADialog" ) )
+ return
+
+ if ( IsDialog( uiGlobal.activeMenu ) )
+ CloseActiveMenu( true )
+
+ uiGlobal.consoleSettingMenu = eConsoleSettingsMenu.AUDIO_VISUAL_MENU
+
+ EULA_Dialog()
+ return
+ }
+ else
+ {
+ AdvanceMenu( GetMenu( "AudioVideoMenu" ) )
+ return
+ }
+}
+
+void function TrackInstallProgress()
+{
+ var rui = Hud_GetRui( Hud_GetChild( file.panel, "InstallProgress" ) )
+
+ while ( GetGameFullyInstalledProgress() < 1.0 )
+ {
+ file.installing = true
+ RuiSetFloat( rui, "installProgress", GetGameFullyInstalledProgress() )
+ wait 0.5
+ }
+
+ file.installing = false
+ RuiSetFloat( rui, "installProgress", 1.0 )
+}
+
+bool function IsStryderAuthenticated()
+{
+ return GetConVarInt( "mp_allowed" ) != -1
+}
+
+bool function IsStryderAllowingMP()
+{
+ return GetConVarInt( "mp_allowed" ) == 1
+}
+
+#if PS4_PROG
+bool function HasLatestPatch()
+{
+ int status = PS4_getUserNetworkingErrorStatus()
+
+ if ( status == -2141913073 ) // SCE_NP_ERROR_LATEST_PATCH_PKG_EXIST
+ return false
+
+ return true
+}
+#endif // PS4_PROG
+
+void function UpdatePromoData()
+{
+ file.promoData = GetMainMenuPromos()
+
+ UpdateWhatsNewData()
+ UpdateSpotlightData()
+}
+
+void function UICodeCallback_MainMenuPromosUpdated()
+{
+ printt( "MainMenuPromos updated" )
+
+ UpdatePromoData()
+}
+
+void function UpdateWhatsNewData()
+{
+ // file.promoData.newInfo_ImageIndex
+ //RuiSetString( file.whatsNew, "line1Text", "`2%$rui/menu/main_menu/whats_new_bulletpoint%`0 Updated Live Fire Maps!\n`2%$rui/menu/main_menu/whats_new_bulletpoint%`0 Prime Titans`0 in the Store\n`2%$rui/menu/main_menu/whats_new_bulletpoint% DOUBLE XP`0 weekend!" )//file.promoData.newInfo_Title1 )
+ RuiSetString( file.whatsNew, "line1Text", file.promoData.newInfo_Title1 )
+ RuiSetString( file.whatsNew, "line2Text", file.promoData.newInfo_Title2 )
+ RuiSetString( file.whatsNew, "line3Text", file.promoData.newInfo_Title3 )
+
+ bool isVisible = true
+ if ( file.promoData.newInfo_Title1 == "" && file.promoData.newInfo_Title2 == "" && file.promoData.newInfo_Title3 == "" )
+ isVisible = false
+
+ RuiSetBool( file.whatsNew, "isVisible", isVisible )
+}
+
+void function UpdateSpotlightData()
+{
+ SetSpotlightButtonData( file.spotlightButtons[0], file.promoData.largeButton_Url, file.promoData.largeButton_ImageIndex, file.promoData.largeButton_Title, file.promoData.largeButton_Text )
+ SetSpotlightButtonData( file.spotlightButtons[1], file.promoData.smallButton1_Url, file.promoData.smallButton1_ImageIndex, file.promoData.smallButton1_Title )
+ SetSpotlightButtonData( file.spotlightButtons[2], file.promoData.smallButton2_Url, file.promoData.smallButton2_ImageIndex, file.promoData.smallButton2_Title )
+}
+
+void function SetSpotlightButtonData( var button, string link, int imageIndex, string title, string details = "skip" )
+{
+ var rui = Hud_GetRui( button )
+
+ var dataTable = GetDataTable( $"datatable/spotlight_images.rpak" )
+ asset image = GetDataTableAsset( dataTable, imageIndex, GetDataTableColumnByName( dataTable, "image" ) )
+
+ RuiSetImage( rui, "buttonImage", image )
+ RuiSetString( rui, "titleText", title )
+
+ if ( details != "skip" )
+ RuiSetString( rui, "detailsText", details )
+
+ button.s.link = link
+}
+
+void function SpotlightButton_Activate( var button )
+{
+ string link = expect string( button.s.link )
+
+ if ( link == "" )
+ return
+
+ if ( link.find( "menu:" ) == 0 )
+ {
+ var menu
+
+ switch ( link )
+ {
+ //case "menu:new":
+ // menu = GetMenu( "StoreMenu_NewReleases" )
+ // break
+
+ case "menu:new":
+ case "menu:weaponskins":
+ menu = GetMenu( "StoreMenu_WeaponSkins" )
+ break
+
+ //case "menu:limited":
+ // menu = GetMenu( "StoreMenu_Limited" )
+ // break
+
+ case "menu:sales":
+ menu = GetMenu( "StoreMenu_Sales" )
+ break
+ }
+
+ if ( menu != null )
+ {
+ uiGlobal.menuToOpenFromPromoButton = menu
+ LaunchMP()
+ }
+ }
+ else
+ {
+ LaunchExternalWebBrowser( link, WEBBROWSER_FLAG_MUTEGAME )
+ }
+}
diff --git a/Northstar.Client/mod/scripts/vscripts/ui/ui_utility.gnut b/Northstar.Client/mod/scripts/vscripts/ui/ui_utility.gnut
new file mode 100644
index 00000000..02d77795
--- /dev/null
+++ b/Northstar.Client/mod/scripts/vscripts/ui/ui_utility.gnut
@@ -0,0 +1,634 @@
+untyped
+
+globalize_all_functions
+
+global const NUM_EOG_CHALLENGE_BOXES = 6
+
+global const WEBBROWSER_FLAG_NONE = 0
+global const WEBBROWSER_FLAG_MUTEGAME = 0x0001
+
+
+function UtilityUI_Init()
+{
+ level.getPersistentVarWrapper <- class
+ {
+ function GetPersistentVar( variable )
+ {
+ // This looks up the code function GetPersistentVar
+ return GetPersistentVar( variable )
+ }
+
+ function GetPersistentVarAsInt( string variable )
+ {
+ return GetPersistentVarAsInt( variable )
+ }
+ }
+}
+
+int[2] function GetScreenSize()
+{
+ var screenSize = Hud_GetSize( GetMenu( "MainMenu" ) )
+
+ int[2] returnSize
+ returnSize[0] = expect int( screenSize[0] )
+ returnSize[1] = expect int( screenSize[1] )
+
+ return returnSize
+}
+
+float[2] function GetContentScaleFactor( var menu )
+{
+ local screenSize = menu.GetSize()
+ float screenSizeX = expect int( screenSize[0] ).tofloat()
+ float screenSizeY = expect int( screenSize[1] ).tofloat()
+ float aspectRatio = screenSizeX / screenSizeY
+ float[2] scaleFactor
+ scaleFactor[0] = screenSizeX / ( 1080.0 * aspectRatio )
+ scaleFactor[1] = screenSizeY / 1080.0
+ return scaleFactor
+}
+
+float function ContentScaledX( int val )
+{
+ return (val * GetContentScaleFactor( GetMenu( "MainMenu" ) )[0])
+}
+
+float function ContentScaledY( int val )
+{
+ return (val * GetContentScaleFactor( GetMenu( "MainMenu" ) )[1])
+}
+
+int function ContentScaledXAsInt( int val )
+{
+ float fVal = val * GetContentScaleFactor( GetMenu( "MainMenu" ) )[0]
+ return int( fVal + 0.5 )
+}
+
+int function ContentScaledYAsInt( int val )
+{
+ float fVal = val * GetContentScaleFactor( GetMenu( "MainMenu" ) )[1]
+ return int( fVal + 0.5 )
+}
+
+// Returns string or null
+function GetLobbyTeamImage( int team )
+{
+ Assert( IsConnected() )
+
+ if ( !GetLobbyTeamsShowAsBalanced() )
+ return null
+
+ if ( GetLobbyTypeScript() == eLobbyType.MATCH || GetLobbyTypeScript() == eLobbyType.PRIVATE_MATCH )
+ return GetTeamImage( team )
+
+ return null
+}
+
+// Returns string or null
+function GetLobbyTeamName( int team )
+{
+ Assert( IsConnected() )
+
+ if ( !GetLobbyTeamsShowAsBalanced() )
+ return null
+
+ if ( GetLobbyTypeScript() == eLobbyType.MATCH || GetLobbyTypeScript() == eLobbyType.PRIVATE_MATCH )
+ return GetTeamName( team )
+
+ return null
+}
+
+asset function GetTeamImage( int team )
+{
+ Assert( team == TEAM_IMC || team == TEAM_MILITIA )
+
+ if ( team == TEAM_IMC )
+ return $"ui/scoreboard_imc_logo"
+
+ return $"ui/scoreboard_mcorp_logo"
+}
+
+function RefreshPersistentFunc( func )
+{
+ for ( int i = 0; i < 5; i++ )
+ {
+ // cause who knows when persistent data changes
+ delaythread( i * 0.1 ) RunFuncWithConnectedCheck( func )
+ }
+}
+
+function RunFuncWithConnectedCheck( func )
+{
+ if ( !IsConnected() )
+ return
+ func()
+}
+
+string function GetActiveLevel()
+{
+ // The level load callbacks overlap with the level init/shutdown callbacks, so we track each one separately.
+ if ( uiGlobal.loadedLevel != "" )
+ return uiGlobal.loadedLevel
+ return uiGlobal.loadingLevel
+}
+
+function HandleLockedMenuItem( menu, button, hideTip = false )
+{
+ /*array<var> elements = GetElementsByClassname( menu, "HideWhenLocked" )
+ var buttonTooltip = Hud_GetChild( menu, "ButtonTooltip" )
+ //var buttonTooltip = GetElementsByClassname( menu, "ButtonTooltip" )[0]
+ var toolTipLabel = Hud_GetChild( buttonTooltip, "Label" )
+
+ entity player = GetUIPlayer()
+ if ( player == null )
+ return
+
+ if ( "ref" in button.s && button.s.ref != null && IsItemLocked( player, expect string( button.s.ref ) ) && !hideTip )
+ {
+ foreach( elem in elements )
+ Hud_Hide( elem )
+
+ Hud_SetText( toolTipLabel, "#UNLOCKED_AT_LEVEL", g_unlocks[ expect string( button.s.ref ) ].unlockLevel, "" )
+
+ local buttonPos = Hud_GetAbsPos( button )
+ local buttonHeight = Hud_GetHeight( button )
+ local tooltipHeight = Hud_GetHeight( buttonTooltip )
+ local yOffset = ( tooltipHeight - buttonHeight ) / 2.0
+
+ Hud_SetPos( buttonTooltip, buttonPos[0] + Hud_GetWidth( button ) * 0.9, buttonPos[1] - yOffset )
+ Hud_Show( buttonTooltip )
+
+ return true
+ }
+ else
+ {
+ foreach( elem in elements )
+ Hud_Show( elem )
+ Hud_Hide( buttonTooltip )
+ }
+ return false*/
+}
+
+// No way to test a named element exists so this is a workaround
+var function GetSingleElementByClassname( var menu, string classname )
+{
+ array<var> elems = GetElementsByClassname( menu, classname )
+
+ if ( elems.len() )
+ {
+ Assert( elems.len() == 1 )
+ return elems[0]
+ }
+
+ return null
+}
+
+array function GetElementsByClassnameForMenus( string classname, array menus )
+{
+ array elements = []
+
+ foreach ( menu in menus )
+ elements.extend( GetElementsByClassname( menu, classname ) )
+
+ return elements
+}
+
+function WaitFrameOrUntilLevelLoaded()
+{
+ WaitFrame()
+
+ while ( uiGlobal.loadedLevel == "" )
+ WaitFrame()
+}
+
+bool function IsPlayerAlone()
+{
+ int myTeam = GetTeam()
+ if ( GetTeamSize( myTeam ) <= 1 )
+ return true
+
+ return false
+}
+
+bool function PartyHasMembers()
+{
+ if ( GetPartySize() > 1 )
+ return true
+
+ return false
+}
+
+bool function AmIPartyMember()
+{
+ return ( PartyHasMembers() && !AmIPartyLeader() )
+}
+
+string function GetGameModeDisplayName( string mode )
+{
+ string displayName = GetGamemodeVarOrUseValue( mode, "name", (mode in GAMETYPE_TEXT) ? GAMETYPE_TEXT[mode] : "" )
+
+ // modification to support playlists too
+ if ( displayName == "" )
+ return GetPlaylistVarOrUseValue( mode, "name", "" )
+
+ return displayName
+}
+
+string function GetGameModeDisplayDesc( string mode ) //TODO: Make this support attack/defend descriptions
+{
+ string displayDesc = GetGamemodeVarOrUseValue( mode, "description", (mode in GAMETYPE_DESC) ? GAMETYPE_DESC[mode] : "" )
+
+ // modification to support playlists too
+ if ( displayDesc == "" )
+ {
+ displayDesc = Localize( GetPlaylistVarOrUseValue( mode, "description", "" ) )
+
+ if ( displayDesc.find( "^" ) != null )
+ displayDesc = displayDesc.slice( 0, displayDesc.find( "^" ) )
+ }
+
+ return displayDesc
+}
+
+string function GetGameModeDisplayHint( string mode ) //TODO: Make this support attack/defend descriptions
+{
+ string displayDesc = GetGamemodeVarOrUseValue( mode, "hint", GetGameModeDisplayDesc( mode ) )
+ return displayDesc
+}
+
+asset function GetGameModeDisplayImage( string mode )
+{
+ return GAMETYPE_ICON[ mode ]
+}
+
+array<int> function ColorStringToArray( string colorString )
+{
+ array<string> tokens = split( colorString, " " )
+
+ Assert( tokens.len() >= 3 && tokens.len() <= 4 )
+
+ array<int> colorArray
+ foreach ( token in tokens )
+ colorArray.append( int( token ) )
+
+ return colorArray
+}
+
+array<int> function GetGameModeDisplayColor( string mode )
+{
+ array<int> displayColor = ColorStringToArray( GetGamemodeVarOrUseValue( mode, "color", "" ) )
+ if ( displayColor.len() == 0 )
+ displayColor = GameMode_GetColor( mode )
+
+ return displayColor
+}
+
+float function GetCurrentPlaylistVarFloat( val, useVal )
+{
+ expect string( val )
+ expect float( useVal )
+
+ local result = GetCurrentPlaylistVarOrUseValue( val, useVal )
+ if ( result == null )
+ return 0.0
+
+ return float( result )
+}
+
+// ???: player not used
+bool function PlayerProgressionAllowed( player = null )
+{
+ if ( IsPrivateMatch() )
+ return false
+
+ return true
+}
+
+entity function GetUIPlayer()
+{
+ if ( !IsFullyConnected() )
+ return null
+
+ return GetLocalClientPlayer()
+}
+
+
+int function GetLobbyTypeScript()
+{
+ if ( GetLobbyType() == "game" )
+ {
+ if ( IsPrivateMatch() )
+ return eLobbyType.PRIVATE_MATCH
+ else
+ return eLobbyType.MATCH
+ }
+ else
+ {
+ if ( AmIPartyLeader() )
+ {
+ if ( IsPlayerAlone() ) // TODO: This function was changed to only check your team size, not the true player count, so you'll probably now have access to some menus you shouldn't be able to.
+ return eLobbyType.SOLO
+ else
+ return eLobbyType.PARTY_LEADER
+ }
+ else
+ {
+ return eLobbyType.PARTY_MEMBER
+ }
+ }
+
+ unreachable
+}
+
+function AddMenu( blockName, asset resourceFile, void functionref() initFunc = null, string displayName = "" )
+{
+ local menu = CreateMenu( "menu_" + blockName, resourceFile )
+ uiGlobal.menus[blockName] <- menu
+ menu.SetHudName( blockName )
+
+ if ( displayName != "" )
+ menu.SetDisplayName( displayName )
+ else
+ menu.SetDisplayName( blockName )
+
+ uiGlobal.allMenus.append( menu )
+
+ MenuDef defaultMenuData
+ uiGlobal.menuData[ menu ] <- defaultMenuData
+
+ if ( initFunc != null )
+ uiGlobal.menuData[ menu ].initFunc = initFunc
+
+ return menu
+}
+
+function AddMenu_WithCreateFunc( blockName, asset resourceFile, void functionref() initFunc, createMenuFunc )
+{
+ local menu = createMenuFunc( "menu_" + blockName, resourceFile )
+ uiGlobal.menus[blockName] <- menu
+ menu.SetHudName( blockName )
+
+ uiGlobal.allMenus.append( menu )
+
+ MenuDef defaultMenuData
+ uiGlobal.menuData[ menu ] <- defaultMenuData
+
+ if ( initFunc != null )
+ uiGlobal.menuData[ menu ].initFunc = initFunc
+
+ return menu
+}
+
+function AddPanel( var menu, string panelName, void functionref() initFunc = null )
+{
+ //printt( "AddPanel called, panelName:", panelName )
+
+ var panel = Hud_GetChild( menu, panelName )
+ uiGlobal.panels[ panelName ] <- panel
+ uiGlobal.allPanels.append( panel )
+
+ PanelDef defaultPanelData
+ uiGlobal.panelData[ panel ] <- defaultPanelData
+
+ if ( initFunc != null )
+ uiGlobal.panelData[ panel ].initFunc = initFunc
+
+ return panel
+}
+
+function AddSubmenu( blockName, asset resourceFile, void functionref() initFunc = null )
+{
+ local menu = CreateMenu( "menu_" + blockName, resourceFile )
+ uiGlobal.menus[blockName] <- menu
+ menu.SetHudName( blockName )
+ menu.SetType( "submenu" )
+
+ menu.s.newFocusRef <- null
+
+ uiGlobal.allMenus.append( menu )
+
+ MenuDef defaultMenuData
+ uiGlobal.menuData[ menu ] <- defaultMenuData
+
+ if ( initFunc != null )
+ uiGlobal.menuData[ menu ].initFunc = initFunc
+
+ return menu
+}
+
+function ClearButton( button )
+{
+ Hud_SetText( button, "" )
+ Hud_SetEnabled( button, false )
+ Hud_SetLocked( button, false )
+ Hud_SetNew( button, false )
+ Hud_SetSelected( button, false )
+}
+
+void function HudElem_SetText( var hudelem, string text )
+{
+ hudelem.SetText( text )
+}
+
+void function SetButtonRuiText( var elem, string text )
+{
+ var rui = Hud_GetRui( elem )
+ RuiSetString( rui, "buttonText", text )
+}
+
+void function SetLabelRuiText( var elem, string text )
+{
+ var rui = Hud_GetRui( elem )
+ RuiSetString( rui, "labelText", text )
+}
+
+void function SetNamedRuiText( var elem, string name, string text )
+{
+ var rui = Hud_GetRui( elem )
+ RuiSetString( rui, name, text )
+}
+
+void function SetNamedRuiBool( var elem, string name, bool state )
+{
+ var rui = Hud_GetRui( elem )
+ RuiSetBool( rui, name, state )
+}
+
+void function SetNamedRuiImage( var elem, string name, asset assetName )
+{
+ var rui = Hud_GetRui( elem )
+ RuiSetImage( rui, name, assetName )
+}
+
+// Should be using Hud_GetChild() instead of this
+var function GetMenuChild( var elem, string name )
+{
+ return elem.GetChild( name )
+}
+
+bool function IsWeaponButton( var button )
+{
+ array<var> weaponButtons = GetElementsByClassname( GetParentMenu( button ), "WeaponSelectClass" )
+
+ foreach ( weaponButton in weaponButtons )
+ {
+ if ( button == weaponButton )
+ return true
+ }
+
+ return false
+}
+
+void function SetPanelTabTitle( var panel, string title )
+{
+ uiGlobal.panelData[ panel ].tabTitle = title
+}
+
+string function GetPanelTabTitle( var panel )
+{
+ return uiGlobal.panelData[ panel ].tabTitle
+}
+
+void function ScriptCallback_UnlockAchievement( int achievementID )
+{
+ Assert( achievementID > 0 && achievementID < achievements.MAX_ACHIVEMENTS, "Tried to unlock achievement with invalid enum value" )
+
+ #if DEV
+ string ref
+ foreach( string _ref, int val in achievements )
+ {
+ if ( val != achievementID )
+ continue
+ ref = _ref
+ break
+ }
+ printt( "#############################################" )
+ printt( "UNLOCKED ACHIEVEMENT:", ref, "(" + achievementID + ")" )
+ printt( "#############################################" )
+ #endif //DEV
+
+ if ( Script_IsRunningTrialVersion() )
+ {
+ printt( "Achievements not awarded in trial version" )
+ return
+ }
+
+
+ Plat_UnlockAchievementByID( achievementID )
+}
+
+void function TryUnlockCollectiblesAchievement()
+{
+ int totalLionsCollectedForGame = GetTotalLionsCollected()
+
+ if ( totalLionsCollectedForGame >= GetTotalLionsInGame() )
+ ScriptCallback_UnlockAchievement( achievements.COLLECTIBLES_3 )
+
+ if ( totalLionsCollectedForGame >= ACHIEVEMENT_COLLECTIBLES_2_COUNT )
+ ScriptCallback_UnlockAchievement( achievements.COLLECTIBLES_2 )
+
+ if ( totalLionsCollectedForGame >= ACHIEVEMENT_COLLECTIBLES_1_COUNT )
+ ScriptCallback_UnlockAchievement( achievements.COLLECTIBLES_1 )
+}
+
+void function TryUnlockCompletedGameAchievements()
+{
+ if ( Script_IsRunningTrialVersion() )
+ return
+
+ var dataTable = GetDataTable( $"datatable/sp_levels.rpak" )
+ int numRows = GetDatatableRowCount( dataTable )
+
+ // Check Completed All Levels Achievement
+ int normalCompleted = 0
+ int hardCompleted = 0
+ int masterCompleted = 0
+ for ( int i=0; i<numRows; i++ )
+ {
+ int levelNum = GetDataTableInt( dataTable, i, GetDataTableColumnByName( dataTable, "levelNum" ) )
+
+ // always count training as master completed
+ if ( GetCompletedDifficultyForBSPNum( i, "sp_missionMasterCompletion" ) || levelNum == 0 )
+ masterCompleted++
+ if ( GetCompletedDifficultyForBSPNum( i, "sp_missionHardCompletion" ) || levelNum == 0 )
+ hardCompleted++
+ if ( GetCompletedDifficultyForBSPNum( i, "sp_missionNormalCompletion" ) || levelNum == 0 )
+ normalCompleted++
+ }
+
+ if ( normalCompleted >= numRows )
+ ScriptCallback_UnlockAchievement( achievements.COMPLETE_NORMAL )
+
+ if ( hardCompleted >= numRows )
+ ScriptCallback_UnlockAchievement( achievements.COMPLETE_HARD )
+
+ if ( masterCompleted >= numRows )
+ ScriptCallback_UnlockAchievement( achievements.COMPLETE_MASTER )
+}
+
+void function PopUpOriginOverlayDisabledDialog()
+{
+ DialogData dialogData
+ dialogData.header = "#ORIGIN_OVERLAY_DISABLED"
+ AddDialogButton( dialogData, "#OK" )
+ OpenDialog( dialogData )
+}
+
+void function PrintPartyData()
+{
+ Party partyData = GetParty()
+
+ printt( "PARTY DATA:" )
+
+ if ( partyData.numSlots == 0 )
+ {
+ printt( " INVALID, numSlots: 0" )
+ return
+ }
+
+ printt( " partyType: ", partyData.partyType )
+ printt( " playlistName: ", partyData.playlistName )
+ printt( " originatorName: ", partyData.originatorName )
+ printt( " originatorUID: ", partyData.originatorUID )
+ printt( " numSlots: ", partyData.numSlots )
+ printt( " numClaimedSlots:", partyData.numClaimedSlots )
+ printt( " numFreeSlots: ", partyData.numFreeSlots )
+ printt( " timeLeft: ", partyData.timeLeft )
+ printt( " amIInThis: ", partyData.amIInThis )
+ printt( " amILeader: ", partyData.amILeader )
+ printt( " searching: ", partyData.searching )
+ print( " members: " )
+
+ foreach ( index, member in partyData.members )
+ {
+ if ( index != 0 )
+ print( " " )
+
+ printt( member.name, "uid:", member.uid, "callsignIdx:", member.callsignIdx, "skillMu:", member.skillMu )
+ }
+}
+
+void function PlayVideoFullScreen( string video, bool showCaptions = false )
+{
+ PlayVideo( video, 0, 0, 0, 0, showCaptions )
+}
+
+void function PlayVideoUsingPanelRect( string video, var panel )
+{
+ int xPos = Hud_GetX( panel )
+ int yPos = Hud_GetY( panel )
+ int width = Hud_GetWidth( panel )
+ int height = Hud_GetHeight( panel )
+
+ PlayVideo( video, xPos, yPos, width, height, false )
+}
+
+void function Hud_SetNavUp( var buttonFrom, var buttonTo )
+{
+ buttonFrom.SetNavUp( buttonTo )
+}
+
+void function Hud_SetNavDown( var buttonFrom, var buttonTo )
+{
+ buttonFrom.SetNavDown( buttonTo )
+} \ No newline at end of file