aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBobTheBob <32057864+BobTheBob9@users.noreply.github.com>2021-07-07 22:25:59 +0100
committerBobTheBob <32057864+BobTheBob9@users.noreply.github.com>2021-07-07 22:25:59 +0100
commit07b7eafd5c1845c70510b695446c23973fed1d4d (patch)
tree8452fd02d8356a65f07089c20318a8de95975125
parent53194543e43f8a645ba83ddb464028ba0c3b9d70 (diff)
downloadNorthstarMods-07b7eafd5c1845c70510b695446c23973fed1d4d.tar.gz
NorthstarMods-07b7eafd5c1845c70510b695446c23973fed1d4d.zip
add fra, featured modes and some private lobby v2 stuff
-rw-r--r--Northstar.Client/scripts/vscripts/ui/menu_lobby.nut1628
-rw-r--r--Northstar.Client/scripts/vscripts/ui/menu_map_select.nut162
-rw-r--r--Northstar.Client/scripts/vscripts/ui/menu_mode_select.nut135
-rw-r--r--Northstar.Client/scripts/vscripts/ui/ui_utility.gnut634
-rw-r--r--Northstar.Custom/mod.json2
-rw-r--r--Northstar.Custom/scripts/vscripts/gamemodes/_gamemode_gg.gnut2
-rw-r--r--Northstar.Custom/scripts/vscripts/gamemodes/sh_gamemode_ctf_comp.gnut5
-rw-r--r--Northstar.Custom/scripts/vscripts/gamemodes/sh_gamemode_fastball.gnut2
-rw-r--r--Northstar.Custom/scripts/vscripts/gamemodes/sh_gamemode_gg.gnut1
-rw-r--r--Northstar.Custom/scripts/vscripts/gamemodes/sh_gamemode_inf.gnut2
-rw-r--r--Northstar.Custom/scripts/vscripts/gamemodes/sh_gamemode_kr.gnut2
-rw-r--r--Northstar.Custom/scripts/vscripts/gamemodes/sh_gamemode_tt.gnut2
-rw-r--r--Northstar.Custom/scripts/vscripts/lobby/sh_private_lobby_custom_modes_init.gnut11
-rw-r--r--Northstar.CustomServers/mod.json21
-rw-r--r--Northstar.CustomServers/scripts/vscripts/_powerup.gnut2
-rw-r--r--Northstar.CustomServers/scripts/vscripts/gamemodes/_featured_mode_settings.gnut121
-rw-r--r--Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_coliseum.nut87
-rw-r--r--Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_fra.nut25
-rw-r--r--Northstar.CustomServers/scripts/vscripts/gamemodes/sh_gamemodes.gnut5
-rw-r--r--Northstar.CustomServers/scripts/vscripts/lobby/_private_lobby.gnut6
-rw-r--r--Northstar.CustomServers/scripts/vscripts/lobby/sh_lobby.gnut (renamed from bobthebob.testing/scripts/vscripts/lobby/sh_lobby.gnut)0
-rw-r--r--Northstar.CustomServers/scripts/vscripts/lobby/sh_private_lobby_modes_init.gnut21
-rw-r--r--Northstar.CustomServers/scripts/vscripts/sh_northstar_utils.gnut49
-rw-r--r--bobthebob.testing/mod.json6
-rw-r--r--bobthebob.testing/scripts/vscripts/sh_bobtestingfunctions_mp.gnut6
-rw-r--r--bobthebob.testing/scripts/vscripts/ui/menu_map_select.nut108
-rw-r--r--bobthebob.testing/scripts/vscripts/ui/menu_mode_select.nut3
27 files changed, 2960 insertions, 88 deletions
diff --git a/Northstar.Client/scripts/vscripts/ui/menu_lobby.nut b/Northstar.Client/scripts/vscripts/ui/menu_lobby.nut
new file mode 100644
index 00000000..67036d48
--- /dev/null
+++ b/Northstar.Client/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( "ServerBrowser ) ) i guess? idk
+}
+
+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/scripts/vscripts/ui/menu_map_select.nut b/Northstar.Client/scripts/vscripts/ui/menu_map_select.nut
new file mode 100644
index 00000000..7ed0d177
--- /dev/null
+++ b/Northstar.Client/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/scripts/vscripts/ui/menu_mode_select.nut b/Northstar.Client/scripts/vscripts/ui/menu_mode_select.nut
new file mode 100644
index 00000000..23376781
--- /dev/null
+++ b/Northstar.Client/scripts/vscripts/ui/menu_mode_select.nut
@@ -0,0 +1,135 @@
+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 )
+ Hud_SetLocked( buttons[ i ], false )
+
+ 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/scripts/vscripts/ui/ui_utility.gnut b/Northstar.Client/scripts/vscripts/ui/ui_utility.gnut
new file mode 100644
index 00000000..02d77795
--- /dev/null
+++ b/Northstar.Client/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
diff --git a/Northstar.Custom/mod.json b/Northstar.Custom/mod.json
index 75a14291..8d7c1752 100644
--- a/Northstar.Custom/mod.json
+++ b/Northstar.Custom/mod.json
@@ -1,4 +1,4 @@
-{
+disabled{
"ApiId" : "Northstar.Custom",
"Name" : "Northstar.Custom",
"Description" : "Additional content for coop and custom multiplayer servers",
diff --git a/Northstar.Custom/scripts/vscripts/gamemodes/_gamemode_gg.gnut b/Northstar.Custom/scripts/vscripts/gamemodes/_gamemode_gg.gnut
index fd41236c..99a5ccf7 100644
--- a/Northstar.Custom/scripts/vscripts/gamemodes/_gamemode_gg.gnut
+++ b/Northstar.Custom/scripts/vscripts/gamemodes/_gamemode_gg.gnut
@@ -73,6 +73,8 @@ void function OnPlayerKilled( entity victim, entity attacker, var damageInfo )
void function UpdateLoadout( entity player )
{
+ // todo: honestly, this should be reworked to use PilotLoadoutDefs instead of directly modifying weapons and shit
+
int currentWeaponIndex = GameRules_GetTeamScore( player.GetTeam() )
array<GunGameWeapon> weapons = GetGunGameWeapons()
diff --git a/Northstar.Custom/scripts/vscripts/gamemodes/sh_gamemode_ctf_comp.gnut b/Northstar.Custom/scripts/vscripts/gamemodes/sh_gamemode_ctf_comp.gnut
index aff693c7..1a1ce645 100644
--- a/Northstar.Custom/scripts/vscripts/gamemodes/sh_gamemode_ctf_comp.gnut
+++ b/Northstar.Custom/scripts/vscripts/gamemodes/sh_gamemode_ctf_comp.gnut
@@ -25,6 +25,8 @@ void function CreateGamemodeCTFComp()
GameMode_AddScoreboardColumnData( GAMEMODE_CTF_COMP, "#SCOREBOARD_TITAN_DAMAGE", PGS_DISTANCE_SCORE, 6 ) // gotta use a weird pgs here since we're running out of them lol
GameMode_SetColor( GAMEMODE_CTF_COMP, [61, 117, 193, 255] )
+ AddPrivateMatchMode( GAMEMODE_CTF_COMP ) // add to private lobby modes
+
// this gamemode is literally just normal ctf + a few extra settings
// as such we do all the inits in this file, not enough logic to be worth splitting it up
@@ -46,6 +48,9 @@ void function CreateGamemodeCTFComp()
void function CTFCompRegisterNetworkVars()
{
+ if ( GAMETYPE != GAMEMODE_CTF_COMP )
+ return
+
// copied from the vanilla ctf remote functions
RegisterNetworkedVariable( "imcFlag", SNDC_GLOBAL, SNVT_ENTITY )
RegisterNetworkedVariable( "milFlag", SNDC_GLOBAL, SNVT_ENTITY )
diff --git a/Northstar.Custom/scripts/vscripts/gamemodes/sh_gamemode_fastball.gnut b/Northstar.Custom/scripts/vscripts/gamemodes/sh_gamemode_fastball.gnut
index 734e24ce..2462d537 100644
--- a/Northstar.Custom/scripts/vscripts/gamemodes/sh_gamemode_fastball.gnut
+++ b/Northstar.Custom/scripts/vscripts/gamemodes/sh_gamemode_fastball.gnut
@@ -22,6 +22,8 @@ void function CreateGamemodeFastball()
GameMode_AddScoreboardColumnData( GAMEMODE_FASTBALL, "#SCOREBOARD_DEATHS", PGS_DEATHS, 2 )
GameMode_SetColor( GAMEMODE_FASTBALL, [147, 204, 57, 255] )
+ AddPrivateMatchMode( GAMEMODE_FASTBALL ) // add to private lobby modes
+
#if SERVER
GameMode_AddServerInit( GAMEMODE_FASTBALL, GamemodeFastball_Init )
GameMode_SetPilotSpawnpointsRatingFunc( GAMEMODE_FASTBALL, RateSpawnpoints_Generic )
diff --git a/Northstar.Custom/scripts/vscripts/gamemodes/sh_gamemode_gg.gnut b/Northstar.Custom/scripts/vscripts/gamemodes/sh_gamemode_gg.gnut
index 4ea9ac20..c4021a3c 100644
--- a/Northstar.Custom/scripts/vscripts/gamemodes/sh_gamemode_gg.gnut
+++ b/Northstar.Custom/scripts/vscripts/gamemodes/sh_gamemode_gg.gnut
@@ -31,6 +31,7 @@ void function CreateGamemodeGG()
GameMode_AddScoreboardColumnData( GAMEMODE_GG, "#SCOREBOARD_PILOT_KILLS", PGS_PILOT_KILLS, 2 )
GameMode_SetColor( GAMEMODE_GG, [147, 204, 57, 255] )
+ AddPrivateMatchMode( GAMEMODE_GG ) // add to private lobby modes
// setup guns
diff --git a/Northstar.Custom/scripts/vscripts/gamemodes/sh_gamemode_inf.gnut b/Northstar.Custom/scripts/vscripts/gamemodes/sh_gamemode_inf.gnut
index b73dc194..bcd86378 100644
--- a/Northstar.Custom/scripts/vscripts/gamemodes/sh_gamemode_inf.gnut
+++ b/Northstar.Custom/scripts/vscripts/gamemodes/sh_gamemode_inf.gnut
@@ -21,6 +21,8 @@ void function CreateGamemodeInfection()
GameMode_AddScoreboardColumnData( GAMEMODE_INFECTION, "#SCOREBOARD_KILLS", PGS_ASSAULT_SCORE, 2 )
GameMode_SetColor( GAMEMODE_INFECTION, [147, 204, 57, 255] )
+ AddPrivateMatchMode( GAMEMODE_INFECTION ) // add to private lobby modes
+
#if SERVER
GameMode_AddServerInit( GAMEMODE_INFECTION, GamemodeInfection_Init )
GameMode_SetPilotSpawnpointsRatingFunc( GAMEMODE_INFECTION, RateSpawnpoints_Generic )
diff --git a/Northstar.Custom/scripts/vscripts/gamemodes/sh_gamemode_kr.gnut b/Northstar.Custom/scripts/vscripts/gamemodes/sh_gamemode_kr.gnut
index 2a320077..7cd91de9 100644
--- a/Northstar.Custom/scripts/vscripts/gamemodes/sh_gamemode_kr.gnut
+++ b/Northstar.Custom/scripts/vscripts/gamemodes/sh_gamemode_kr.gnut
@@ -19,6 +19,8 @@ void function CreateGamemodeKR()
GameMode_AddScoreboardColumnData( GAMEMODE_KR, "#SCOREBOARD_KR_RECORD", PGS_ASSAULT_SCORE, 2 )
GameMode_AddScoreboardColumnData( GAMEMODE_KR, "#SCOREBOARD_PILOT_KILLS", PGS_PILOT_KILLS, 2 )
GameMode_SetColor( GAMEMODE_KR, [147, 204, 57, 255] )
+
+ AddPrivateMatchMode( GAMEMODE_KR ) // add to private lobby modes
#if SERVER
GameMode_AddServerInit( GAMEMODE_KR, GamemodeKR_Init )
diff --git a/Northstar.Custom/scripts/vscripts/gamemodes/sh_gamemode_tt.gnut b/Northstar.Custom/scripts/vscripts/gamemodes/sh_gamemode_tt.gnut
index 9e879843..f3fbff28 100644
--- a/Northstar.Custom/scripts/vscripts/gamemodes/sh_gamemode_tt.gnut
+++ b/Northstar.Custom/scripts/vscripts/gamemodes/sh_gamemode_tt.gnut
@@ -21,6 +21,8 @@ void function CreateGamemodeTT()
GameMode_AddScoreboardColumnData( GAMEMODE_TT, "#SCOREBOARD_PILOT_KILLS", PGS_PILOT_KILLS, 2 )
GameMode_SetColor( GAMEMODE_TT, [200, 40, 40, 255] )
+ AddPrivateMatchMode( GAMEMODE_TT ) // add to private lobby modes
+
#if SERVER
GameMode_AddServerInit( GAMEMODE_TT, GamemodeTT_Init )
GameMode_SetPilotSpawnpointsRatingFunc( GAMEMODE_TT, RateSpawnpoints_Generic )
diff --git a/Northstar.Custom/scripts/vscripts/lobby/sh_private_lobby_custom_modes_init.gnut b/Northstar.Custom/scripts/vscripts/lobby/sh_private_lobby_custom_modes_init.gnut
new file mode 100644
index 00000000..81c08f22
--- /dev/null
+++ b/Northstar.Custom/scripts/vscripts/lobby/sh_private_lobby_custom_modes_init.gnut
@@ -0,0 +1,11 @@
+global function CustomPrivateMatchModesInit
+
+void function CustomPrivateMatchModesInit()
+{
+ // modes
+ AddPrivateMatchMode( "gg" )
+ AddPrivateMatchMode( "inf" )
+ AddPrivateMatchMode( "kr" )
+ AddPrivateMatchMode( "tt" )
+ AddPrivateMatchMode( "ctf_comp" )
+} \ No newline at end of file
diff --git a/Northstar.CustomServers/mod.json b/Northstar.CustomServers/mod.json
index a9e4f36d..6e4d2d58 100644
--- a/Northstar.CustomServers/mod.json
+++ b/Northstar.CustomServers/mod.json
@@ -24,6 +24,11 @@
},
{
+ "Path": "sh_northstar_utils.gnut",
+ "RunOn": "CLIENT || SERVER || UI",
+ },
+
+ {
"Path": "mp/levels/_lf_maps_shared.gnut",
"RunOn": "SERVER && MP"
},
@@ -32,11 +37,20 @@
"Path": "gamemodes/sh_gamemodes_custom.gnut",
"RunOn": "(CLIENT || SERVER) && MP"
},
-
{
"Path": "sh_remote_functions_mp_custom.gnut",
"RunOn": "(CLIENT || SERVER) && MP"
},
+ {
+ "Path": "gamemodes/_gamemode_fra.nut",
+ "RunOn": "SERVER && MP",
+ "ServerPreCallback": "GamemodeFRA_AddAdditionalInitCallback"
+ },
+ {
+ "Path": "gamemodes/_featured_mode_settings.gnut",
+ "RunOn": "SERVER && MP",
+ "ServerCallback": "FeaturedModeSettings_Init"
+ },
{
"Path": "mp/_classic_mp_dropship_intro.gnut",
@@ -52,5 +66,10 @@
"RunOn": "SERVER && MP",
"ServerCallback": "SvLoadoutsMP_Init"
},
+
+ {
+ "Path": "lobby/sh_private_lobby_modes_init.gnut",
+ "RunOn": "( SERVER || CLIENT ) && MP",
+ },
]
} \ No newline at end of file
diff --git a/Northstar.CustomServers/scripts/vscripts/_powerup.gnut b/Northstar.CustomServers/scripts/vscripts/_powerup.gnut
index a6a117af..03b9fcfc 100644
--- a/Northstar.CustomServers/scripts/vscripts/_powerup.gnut
+++ b/Northstar.CustomServers/scripts/vscripts/_powerup.gnut
@@ -81,7 +81,7 @@ bool function OnPowerupCollected( entity player, entity healthpack )
{
// hack because i couldn't figure out any other way to do this without modifying sh_powerup
// ensure we don't kill the powerup if it's a battery the player can't pickup
- if ( ( powerup.index == ePowerUps.titanTimeReduction || powerup.index == ePowerUps.LTS_TitanTimeReduction ) && PlayerHasBattery( player ) )
+ if ( ( powerup.index == ePowerUps.titanTimeReduction || powerup.index == ePowerUps.LTS_TitanTimeReduction ) && ( player.IsTitan() || PlayerHasMaxBatteryCount( player ) ) )
return false
// idk why the powerup.destroyFunc doesn't just return a bool? would mean they could just handle stuff like this in powerup code
diff --git a/Northstar.CustomServers/scripts/vscripts/gamemodes/_featured_mode_settings.gnut b/Northstar.CustomServers/scripts/vscripts/gamemodes/_featured_mode_settings.gnut
new file mode 100644
index 00000000..27204380
--- /dev/null
+++ b/Northstar.CustomServers/scripts/vscripts/gamemodes/_featured_mode_settings.gnut
@@ -0,0 +1,121 @@
+untyped
+global function FeaturedModeSettings_Init
+
+void function FeaturedModeSettings_Init()
+{
+ // if it's not super obvious at a glance this script is used for playlist vars with the prefix "featured_mode_"
+ // these often set loadouts and shit so they need a script
+ // note: for turbo_titans, the core multiplier is set in playlist
+
+ AddCallback_OnPlayerRespawned( FeaturedModeSettingsSetupPilotLoadouts )
+ AddCallback_OnPilotBecomesTitan( FeaturedModeSettingsSetupTitanLoadouts )
+}
+
+bool function IsFeaturedMode( string modeName )
+{
+ return GetCurrentPlaylistVar( "featured_mode_" + modeName ) == "1"
+}
+
+void function FeaturedModeSettingsSetupPilotLoadouts( entity player )
+{
+ bool shouldChangeLoadout = false
+
+ // create loadout struct
+ PilotLoadoutDef modifiedLoadout = clone GetActivePilotLoadout( player )
+
+ if ( IsFeaturedMode( "all_holopilot" ) )
+ {
+ shouldChangeLoadout = true
+
+ modifiedLoadout.special = "mp_ability_holopilot"
+ }
+
+ if ( IsFeaturedMode( "all_grapple" ) )
+ {
+ shouldChangeLoadout = true
+
+ modifiedLoadout.special = "mp_ability_grapple"
+ modifiedLoadout.specialMods = [ "all_grapple" ]
+ }
+
+ if ( IsFeaturedMode( "all_phase" ) )
+ {
+ shouldChangeLoadout = true
+
+ modifiedLoadout.special = "mp_ability_shifter"
+ modifiedLoadout.specialMods = [ "all_phase" ]
+ }
+
+ if ( IsFeaturedMode( "all_ticks" ) )
+ {
+ shouldChangeLoadout = true
+
+ modifiedLoadout.ordnance = "mp_weapon_frag_drone"
+ modifiedLoadout.ordnanceMods = [ "all_ticks" ]
+ }
+
+ if ( IsFeaturedMode( "rocket_arena" ) )
+ {
+ shouldChangeLoadout = true
+
+ modifiedLoadout.primary = "mp_weapon_epg"
+ modifiedLoadout.primaryMods = [ "rocket_arena" ]
+
+ // set secondary to whatever one is pistol
+ if ( GetWeaponInfoFileKeyField_Global( player.GetMainWeapons()[ 1 ].GetWeaponClassName(), "menu_category" ) == "at" )
+ {
+ modifiedLoadout.weapon3 = "mp_weapon_autopistol"
+ modifiedLoadout.weapon3Mods = [ "rocket_arena" ]
+ }
+ else
+ {
+ modifiedLoadout.secondary = "mp_weapon_autopistol"
+ modifiedLoadout.secondaryMods = [ "rocket_arena" ]
+ }
+ }
+
+ if ( IsFeaturedMode( "shotguns_snipers" ) )
+ {
+
+ shouldChangeLoadout = true
+
+ // this one was never released, assuming it just gives you a mastiff and a kraber with quick swap
+ modifiedLoadout.primary = "mp_weapon_sniper"
+ modifiedLoadout.primaryMods = [ "pas_fast_swap", "pas_fast_ads" ]
+
+ // set secondary to whatever one is pistol
+ if ( GetWeaponInfoFileKeyField_Global( player.GetMainWeapons()[ 1 ].GetWeaponClassName(), "menu_category" ) == "at" )
+ {
+ modifiedLoadout.weapon3 = "mp_weapon_mastiff"
+ modifiedLoadout.weapon3Mods = [ "pas_fast_swap", "pas_run_and_gun" ]
+ }
+ else
+ {
+ modifiedLoadout.secondary = "mp_weapon_mastiff"
+ modifiedLoadout.secondaryMods = [ "pas_fast_swap", "pas_run_and_gun" ]
+ }
+ }
+
+ // dont wanna give a new loadout if it's not necessary, could break other callbacks
+ if ( shouldChangeLoadout )
+ GivePilotLoadout( player, modifiedLoadout )
+
+ if ( IsFeaturedMode( "tactikill" ) )
+ player.GiveExtraWeaponMod( "tactical_cdr_on_kill" )
+
+ if ( IsFeaturedMode( "amped_tacticals" ) )
+ player.GiveExtraWeaponMod( "amped_tacticals" )
+}
+
+void function FeaturedModeSettingsSetupTitanLoadouts( entity player, entity titan )
+{
+ // this doesn't work atm, figure out how it should work and fix at some point
+ entity soul = player.GetTitanSoul()
+ if ( IsFeaturedMode( "turbo_titans" ) )
+ {
+ if ( GetSoulTitanSubClass( soul ) == "stryder" || GetSoulTitanSubClass( soul ) == "atlas" )
+ GivePassive( player, ePassives.PAS_MOBILITY_DASH_CAPACITY )
+ else
+ GivePassive( player, ePassives.PAS_DASH_RECHARGE )
+ }
+} \ No newline at end of file
diff --git a/Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_coliseum.nut b/Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_coliseum.nut
index d8ccfc42..b358cfe8 100644
--- a/Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_coliseum.nut
+++ b/Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_coliseum.nut
@@ -1,12 +1,95 @@
+untyped
+
global function GamemodeColiseum_Init
global function GamemodeColiseum_CustomIntro
+bool hasShownIntroScreen = false
+
void function GamemodeColiseum_Init()
{
+ // gamemode settings
+ SetRoundBased( true )
+ SetRespawnsEnabled( false )
+ SetShouldUseRoundWinningKillReplay( true )
+ Riff_ForceTitanAvailability( eTitanAvailability.Never )
+ Riff_ForceBoostAvailability( eBoostAvailability.Disabled )
+ Riff_ForceSetEliminationMode( eEliminationMode.Pilots )
+ SetLoadoutGracePeriodEnabled( false ) // prevent modifying loadouts with grace period
+ SetWeaponDropsEnabled( false )
+
+ ClassicMP_SetCustomIntro( ClassicMP_DefaultNoIntro_Setup, ClassicMP_DefaultNoIntro_GetLength() )
+ AddCallback_GameStateEnter( eGameState.Prematch, ShowColiseumIntroScreen )
+ AddCallback_OnPlayerRespawned( GivePlayerColiseumLoadout )
+}
+
+// stub function referenced in sh_gamemodes_mp
+void function GamemodeColiseum_CustomIntro( entity player )
+{}
+void function ShowColiseumIntroScreen()
+{
+ if ( !hasShownIntroScreen )
+ thread ShowColiseumIntroScreenThreaded()
+
+ hasShownIntroScreen = true
}
-void function GamemodeColiseum_CustomIntro(entity _0)
+void function ShowColiseumIntroScreenThreaded()
{
+ wait 5
+
+ foreach ( entity player in GetPlayerArray() )
+ Remote_CallFunction_NonReplay( player, "ServerCallback_ColiseumIntro", 1, 1, 1 ) // stub numbers atm because lazy
+}
+
+void function GivePlayerColiseumLoadout( entity player )
+{
+ // create loadout struct
+ PilotLoadoutDef coliseumLoadout = clone GetActivePilotLoadout( player )
+
+ /* from playlists.txt
+ coliseum_primary "mp_weapon_lstar"
+ coliseum_primary_attachment ""
+ coliseum_primary_mod1 ""
+ coliseum_primary_mod2 ""
+ coliseum_primary_mod3 ""
+ coliseum_secondary "mp_weapon_softball"
+ coliseum_secondary_mod1 ""
+ coliseum_secondary_mod2 ""
+ coliseum_secondary_mod3 ""
+ coliseum_weapon3 ""
+ coliseum_weapon3_mod1 ""
+ coliseum_weapon3_mod2 ""
+ coliseum_weapon3_mod3 ""
+ coliseum_melee "melee_pilot_emptyhanded"
+ coliseum_special "mp_ability_heal"
+ coliseum_ordnance "mp_weapon_frag_drone"
+ coliseum_passive1 "pas_fast_health_regen"
+ coliseum_passive2 "pas_wallhang"*/
+
+ coliseumLoadout.primary = GetColiseumItem( "primary" )
+ coliseumLoadout.primaryMods = [ GetColiseumItem( "primary_attachment" ), GetColiseumItem( "primary_mod1" ), GetColiseumItem( "primary_mod2" ), GetColiseumItem( "primary_mod3" ) ]
+
+ coliseumLoadout.secondary = GetColiseumItem( "secondary" )
+ coliseumLoadout.secondaryMods = [ GetColiseumItem( "secondary_mod1" ), GetColiseumItem( "secondary_mod2" ), GetColiseumItem( "secondary_mod3" ) ]
+
+ coliseumLoadout.weapon3 = GetColiseumItem( "weapon3" )
+ coliseumLoadout.weapon3Mods = [ GetColiseumItem( "weapon3_mod1" ), GetColiseumItem( "weapon3_mod2" ), GetColiseumItem( "weapon3_mod3" ) ]
+
+ coliseumLoadout.melee = GetColiseumItem( "melee" )
+ coliseumLoadout.special = GetColiseumItem( "special" )
+ coliseumLoadout.ordnance = GetColiseumItem( "ordnance" )
+ coliseumLoadout.passive1 = GetColiseumItem( "passive1" )
+ coliseumLoadout.passive2 = GetColiseumItem( "passive2" )
+
+ coliseumLoadout.setFile = GetSuitAndGenderBasedSetFile( "coliseum", coliseumLoadout.race == RACE_HUMAN_FEMALE ? "female" : "male" )
+
+ GivePilotLoadout( player, coliseumLoadout )
+}
+
+string function GetColiseumItem( string name )
+{
+ return expect string ( GetCurrentPlaylistVar( "coliseum_" + name ) )
+}
-} \ No newline at end of file
+// todo this needs the outro: unsure what anims it uses \ No newline at end of file
diff --git a/Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_fra.nut b/Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_fra.nut
new file mode 100644
index 00000000..15f749ac
--- /dev/null
+++ b/Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_fra.nut
@@ -0,0 +1,25 @@
+global function GamemodeFRA_AddAdditionalInitCallback
+
+// fra doesn't register a gamemode init by default, adding one just so we can set stuff up for it
+void function GamemodeFRA_AddAdditionalInitCallback()
+{
+ AddCallback_OnCustomGamemodesInit( GamemodeFRA_AddAdditionalInit )
+}
+
+void function GamemodeFRA_AddAdditionalInit()
+{
+ GameMode_AddServerInit( FREE_AGENCY, GamemodeFRA_Init )
+}
+
+void function GamemodeFRA_Init()
+{
+ // need a way to disable passive earnmeter gain
+ ScoreEvent_SetEarnMeterValues( "PilotBatteryPickup", 0.0, 0.34 )
+
+ AddCallback_OnPlayerKilled( FRARemoveEarnMeter )
+}
+
+void function FRARemoveEarnMeter( entity victim, entity attacker, var damageInfo )
+{
+ PlayerEarnMeter_Reset( victim )
+} \ No newline at end of file
diff --git a/Northstar.CustomServers/scripts/vscripts/gamemodes/sh_gamemodes.gnut b/Northstar.CustomServers/scripts/vscripts/gamemodes/sh_gamemodes.gnut
index df7acb78..9114fcad 100644
--- a/Northstar.CustomServers/scripts/vscripts/gamemodes/sh_gamemodes.gnut
+++ b/Northstar.CustomServers/scripts/vscripts/gamemodes/sh_gamemodes.gnut
@@ -415,7 +415,10 @@ void function GameModes_Init()
#endif
#if SERVER || CLIENT
- InitCustomGamemodes()
+ // add modes/maps/playlists to private lobby that aren't there by default
+ PrivateMatchModesInit()
+
+ InitCustomGamemodes() // do custom gamemode callbacks
GameModes_Init_SV_CL()
#endif
diff --git a/Northstar.CustomServers/scripts/vscripts/lobby/_private_lobby.gnut b/Northstar.CustomServers/scripts/vscripts/lobby/_private_lobby.gnut
index c428d309..8d803da1 100644
--- a/Northstar.CustomServers/scripts/vscripts/lobby/_private_lobby.gnut
+++ b/Northstar.CustomServers/scripts/vscripts/lobby/_private_lobby.gnut
@@ -102,7 +102,11 @@ void function StartMatch()
WaitFrame()
}
- GameRules_SetGameMode( file.mode )
+ if ( file.mode in GAMETYPE_TEXT )
+ GameRules_SetGameMode( file.mode )
+ else
+ GameRules_SetGameMode( GetPlaylistGamemodeByIndex( file.mode, 0 ) )
+
try
{
// todo: not every gamemode uses the same playlist as their name! need some code to resolve these manually
diff --git a/bobthebob.testing/scripts/vscripts/lobby/sh_lobby.gnut b/Northstar.CustomServers/scripts/vscripts/lobby/sh_lobby.gnut
index 24436017..24436017 100644
--- a/bobthebob.testing/scripts/vscripts/lobby/sh_lobby.gnut
+++ b/Northstar.CustomServers/scripts/vscripts/lobby/sh_lobby.gnut
diff --git a/Northstar.CustomServers/scripts/vscripts/lobby/sh_private_lobby_modes_init.gnut b/Northstar.CustomServers/scripts/vscripts/lobby/sh_private_lobby_modes_init.gnut
new file mode 100644
index 00000000..b1474a15
--- /dev/null
+++ b/Northstar.CustomServers/scripts/vscripts/lobby/sh_private_lobby_modes_init.gnut
@@ -0,0 +1,21 @@
+global function PrivateMatchModesInit
+
+void function PrivateMatchModesInit()
+{
+ // modes
+ AddPrivateMatchMode( "ffa" )
+ AddPrivateMatchMode( "fra" )
+ AddPrivateMatchMode( "coliseum" )
+
+ // playlists
+ AddPrivateMatchMode( "attdm" )
+ AddPrivateMatchMode( "turbo_ttdm" )
+ AddPrivateMatchMode( "alts" )
+ AddPrivateMatchMode( "turbo_lts" )
+ AddPrivateMatchMode( "rocket_lf" )
+ AddPrivateMatchMode( "holopilot_lf" )
+
+ // maps
+ AddPrivateMatchMap( "mp_coliseum" )
+ AddPrivateMatchMap( "mp_coliseum_column" )
+} \ No newline at end of file
diff --git a/Northstar.CustomServers/scripts/vscripts/sh_northstar_utils.gnut b/Northstar.CustomServers/scripts/vscripts/sh_northstar_utils.gnut
new file mode 100644
index 00000000..15eed9b2
--- /dev/null
+++ b/Northstar.CustomServers/scripts/vscripts/sh_northstar_utils.gnut
@@ -0,0 +1,49 @@
+globalize_all_functions
+
+enum eNorthstarLobbyType
+{
+ PrivateMatchLobby, // normal vanilla private lobby
+ IntermissionLobby, // similar to tf1's intermission lobby, chooses next map automatically
+ CompetitiveLobby // similar to vanilla privates, but with ready up system
+}
+
+// whether the server is a modded, northstar server
+bool function IsNorthstarServer()
+{
+ bool isModded = true // TEMP for testing
+ try
+ {
+ // need this in a trycatch because the var might not exist atm
+ isModded = GetConVarInt( "northstar_is_modded_server" ) == 1
+ } catch ( ex ) {}
+
+ return isModded
+}
+
+// whether the game should return to the lobby on GameRules_EndMatch()
+bool function ShouldReturnToLobby()
+{
+ bool shouldReturnToLobby = false
+ try
+ {
+ // need this in a trycatch because the var might not exist atm
+ shouldReturnToLobby = GetConVarInt( "northstar_should_return_to_lobby" ) == 1
+ } catch ( ex ) {}
+
+ return shouldReturnToLobby
+}
+
+int function GetNorthstarLobbyType()
+{
+ if ( !IsNorthstarServer() )
+ return eNorthstarLobbyType.PrivateMatchLobby
+
+ int lobbyType = eNorthstarLobbyType.PrivateMatchLobby
+ try
+ {
+ // need this in a trycatch because the var might not exist atm
+ lobbyType = GetConVarInt( "northstar_lobby_type" )
+ } catch ( ex ) {}
+
+ return lobbyType
+} \ No newline at end of file
diff --git a/bobthebob.testing/mod.json b/bobthebob.testing/mod.json
index f79cb091..b0da73e5 100644
--- a/bobthebob.testing/mod.json
+++ b/bobthebob.testing/mod.json
@@ -30,11 +30,5 @@
"ServerPreCallback": "BleedoutTest_Init",
"ClientPreCallback": "BleedoutTest_Init",
},
-
- {
- "Path": "sh_northstar_utils.gnut",
- "RunOn": "CLIENT || SERVER || UI",
- }
-
]
} \ No newline at end of file
diff --git a/bobthebob.testing/scripts/vscripts/sh_bobtestingfunctions_mp.gnut b/bobthebob.testing/scripts/vscripts/sh_bobtestingfunctions_mp.gnut
index 124c7a54..70bc5165 100644
--- a/bobthebob.testing/scripts/vscripts/sh_bobtestingfunctions_mp.gnut
+++ b/bobthebob.testing/scripts/vscripts/sh_bobtestingfunctions_mp.gnut
@@ -125,4 +125,10 @@ void function TestObjectiveRUIFW()
RuiDestroy( rui )
}
+
+void function RespawnWhy()
+{
+ for ( int i = 0; i < 500; i++ ) // this only works in lobby, luckily
+ GetLocalViewPlayer().ClientCommand( "test_clientsetplaylistvaroverride " + i + " whyyyyy" )
+}
#endif \ No newline at end of file
diff --git a/bobthebob.testing/scripts/vscripts/ui/menu_map_select.nut b/bobthebob.testing/scripts/vscripts/ui/menu_map_select.nut
index 7263a9fa..7ed0d177 100644
--- a/bobthebob.testing/scripts/vscripts/ui/menu_map_select.nut
+++ b/bobthebob.testing/scripts/vscripts/ui/menu_map_select.nut
@@ -5,16 +5,9 @@ global function MenuMapSelect_Init
global function InitMapsMenu
-const int MAPS_PER_PAGE = 21
-
-const MAP_LIST_VISIBLE_ROWS = 21
-const MAP_LIST_SCROLL_SPEED = 0
-
struct {
- var menu = null
- array<var> buttons
- int numMapButtonsOffScreen
- int mapListScrollState = 0
+ 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
@@ -27,8 +20,7 @@ function MenuMapSelect_Init()
void function InitMapsMenu()
{
- file.menu = GetMenu( "MapsMenu" )
- var menu = file.menu
+ var menu = GetMenu( "MapsMenu" )
AddMenuEventHandler( menu, eUIEvent.MENU_OPEN, OnOpenMapsMenu )
AddMenuEventHandler( menu, eUIEvent.MENU_CLOSE, OnCloseMapsMenu )
@@ -36,13 +28,9 @@ void function InitMapsMenu()
AddEventHandlerToButtonClass( menu, "MapButtonClass", UIE_GET_FOCUS, MapButton_Focused )
AddEventHandlerToButtonClass( menu, "MapButtonClass", UIE_LOSE_FOCUS, MapButton_LostFocus )
AddEventHandlerToButtonClass( menu, "MapButtonClass", UIE_CLICK, MapButton_Activate )
- //AddEventHandlerToButtonClass( menu, "MapListScrollUpClass", UIE_CLICK, OnMapListScrollUp_Activate )
- //AddEventHandlerToButtonClass( menu, "MapListScrollDownClass", UIE_CLICK, OnMapListScrollDown_Activate )
AddMenuFooterOption( menu, BUTTON_A, "#A_BUTTON_SELECT" )
AddMenuFooterOption( menu, BUTTON_B, "#B_BUTTON_BACK", "#BACK" )
-
- file.buttons = GetElementsByClassname( menu, "MapButtonClass" )
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 )
@@ -50,19 +38,27 @@ void function InitMapsMenu()
void function OnOpenMapsMenu()
{
- array<var> buttons = file.buttons
- array<string> mapsArray = GetPrivateMatchMaps()
+ if ( IsNorthstarServer() )
+ file.mapsPerPage = 15
+ else
+ file.mapsPerPage = 21
+
+ UpdateVisibleMaps()
+}
- file.numMapButtonsOffScreen = int( max( mapsArray.len() - MAP_LIST_VISIBLE_ROWS, 0 ) )
-// Assert( file.numMapButtonsOffScreen >= 0 )
+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 >= 0 && buttonID < mapsArray.len() )
+ if ( buttonID < file.mapsPerPage && mapID < GetPrivateMatchMaps().len() )
{
- string name = mapsArray[buttonID]
+ string name = mapsArray[ mapID ]
SetButtonRuiText( button, GetMapDisplayName( name ) )
Hud_SetEnabled( button, true )
@@ -86,36 +82,30 @@ void function OnOpenMapsMenu()
Hud_SetEnabled( button, false )
}
- if ( buttonID == level.ui.privatematch_map )
+ if ( mapID == level.ui.privatematch_map )
{
printt( buttonID, mapsArray[buttonID] )
Hud_SetFocused( button )
}
}
-
- //RegisterButtonPressedCallback( MOUSE_WHEEL_UP, OnMapListScrollUp_Activate )
- //RegisterButtonPressedCallback( MOUSE_WHEEL_DOWN, OnMapListScrollDown_Activate )
}
void function OnCloseMapsMenu()
{
- //DeregisterButtonPressedCallback( MOUSE_WHEEL_UP, OnMapListScrollUp_Activate )
- //DeregisterButtonPressedCallback( MOUSE_WHEEL_DOWN, OnMapListScrollDown_Activate )
-
Signal( uiGlobal.signalDummy, "OnCloseMapsMenu" )
}
void function MapButton_Focused( var button )
{
- int buttonID = int( Hud_GetScriptID( button ) )
+ int mapID = int( Hud_GetScriptID( button ) ) + ( file.currentMapPage * file.mapsPerPage )
- var menu = file.menu
+ 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[buttonID]
+ string mapName = mapsArray[ mapID ]
asset mapImage = GetMapImageForMapName( mapName )
RuiSetImage( Hud_GetRui( nextMapImage ), "basicImage", mapImage )
@@ -128,36 +118,24 @@ void function MapButton_Focused( var button )
else
Hud_SetText( nextMapDesc, GetMapDisplayDesc( mapName ) )
- // Update window scrolling if we highlight a map not in view
- int minScrollState = int( clamp( buttonID - (MAP_LIST_VISIBLE_ROWS - 1), 0, file.numMapButtonsOffScreen ) )
- int maxScrollState = int( clamp( buttonID, 0, file.numMapButtonsOffScreen ) )
-
- if ( file.mapListScrollState < minScrollState )
- file.mapListScrollState = minScrollState
- if ( file.mapListScrollState > maxScrollState )
- file.mapListScrollState = maxScrollState
-
- UpdateMapListScroll()
}
void function MapButton_LostFocus( var button )
{
- HandleLockedCustomMenuItem( file.menu, button, [], true )
+ 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]
+ string mapName = mapsArray[ mapID + ( file.currentMapPage * file.mapsPerPage ) ]
printt( mapName, mapID )
@@ -165,36 +143,20 @@ void function MapButton_Activate( var button )
CloseActiveMenu()
}
-
-void function OnMapListScrollUp_Activate( var button )
-{
- file.mapListScrollState--
- if ( file.mapListScrollState < 0 )
- file.mapListScrollState = 0
-
- UpdateMapListScroll()
-}
-
-void function OnMapListScrollDown_Activate( var button )
+void function CycleModesBack( var button )
{
- file.mapListScrollState++
- if ( file.mapListScrollState > file.numMapButtonsOffScreen )
- file.mapListScrollState = file.numMapButtonsOffScreen
-
- UpdateMapListScroll()
+ if ( file.currentMapPage == 0 )
+ return
+
+ file.currentMapPage--
+ UpdateVisibleMaps()
}
-function UpdateMapListScroll()
+void function CycleModesForward( var button )
{
- array<var> buttons = file.buttons
- local basePos = buttons[0].GetBasePos()
- local offset = buttons[0].GetHeight() * file.mapListScrollState
-
- buttons[0].SetPos( basePos[0], basePos[1] - offset )
+ if ( ( file.currentMapPage + 1 ) * file.mapsPerPage >= GetPrivateMatchMaps().len() )
+ return
+
+ file.currentMapPage++
+ UpdateVisibleMaps()
}
-
-void function CycleModesBack( var button )
-{}
-
-void function CycleModesForward( var button )
-{}
diff --git a/bobthebob.testing/scripts/vscripts/ui/menu_mode_select.nut b/bobthebob.testing/scripts/vscripts/ui/menu_mode_select.nut
index b6bd6d38..23376781 100644
--- a/bobthebob.testing/scripts/vscripts/ui/menu_mode_select.nut
+++ b/bobthebob.testing/scripts/vscripts/ui/menu_mode_select.nut
@@ -2,7 +2,6 @@ global function InitModesMenu
struct {
int currentModePage
-
} file
const int MODES_PER_PAGE = 15
@@ -105,7 +104,7 @@ void function ModeButton_Click( var button )
int modeID = int( Hud_GetScriptID( button ) ) + ( file.currentModePage * MODES_PER_PAGE )
array<string> modesArray = GetPrivateMatchModes()
- string modeName = modesArray[mapID]
+ 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