diff --git a/README.md b/README.md index 28d57de..de3a197 100644 --- a/README.md +++ b/README.md @@ -78,3 +78,16 @@ Found a bug? Make an issue on GitHub [here.](https://github.com/ToneAPI/pulse/is The Tone API is NOT used by every server, view a list of supported servers [here.](https://tone.sleepycat.date/v2/client/servers) Pulse is very much a W.I.P as of right now, features may be missing/nonfunctional. + +## CHANGELOG + +
+ pulse v2.0.0 "Demeter" + + - Switched to Thunderstore template on the GitHub site for easier development. + - General code rewrite - lots of improvements for easier development. Rewrite includes: + - common code file, for better code readability and ease of development, + - new parser, allowing for better expandability and faster data processing (and much simpler implementation, unlike the hellspawn that was the previous `getFromToneAPI` function), + - new request function, more compact and simple than previous iterations, + - other functions that simplify the code and make it easier to read / develop +
diff --git a/icon.png b/icon.png new file mode 100644 index 0000000..081973f Binary files /dev/null and b/icon.png differ diff --git a/manifest.json b/manifest.json new file mode 100644 index 0000000..af52acd --- /dev/null +++ b/manifest.json @@ -0,0 +1,7 @@ +{ + "name": "Pulse", + "description": "Pulse is a clientside mod for Titanfall 2 letting you view killstats collected by the Tone API using the otherwise non-functional Stats tab.", + "version_number": "2.0.0", + "website_url": "https://github.com/ToneAPI/pulse", + "dependencies": [] +} \ No newline at end of file diff --git a/mod/scripts/vscripts/pulse.gnut b/mod/scripts/vscripts/pulse.gnut deleted file mode 100644 index 4afeeb5..0000000 --- a/mod/scripts/vscripts/pulse.gnut +++ /dev/null @@ -1,119 +0,0 @@ -global function pulseInit -global function GetStatsFromToneAPI -global function getFromToneAPI -global function fetchGamemodeStatsFromToneAPI - -global table pulseData = {} -global table< table > globalToneAPIGamemodeMapData = {} - -global string prefix = "\x1b[38;2;255;178;102m[PULSE]\x1b[0m " - -string HTTPrequestURL = "default" -string currentVersion = "default" -int mapGamemodeEntries = 0 - -struct { - array allMaps -} file - -void function pulseInit() -{ - HTTPrequestURL = GetConVarString("ToneURL") - currentVersion = "v" + GetConVarString("MajorVersion") + "." + GetConVarString("MinorVersion") + "." + GetConVarString("PatchVersion") - HttpRequest getVersion - getVersion.method = HttpRequestMethod.GET - getVersion.url = GetConVarString("VersionURL") - void functionref(HttpRequestResponse) onSuccess = void function(HttpRequestResponse response) - { - table JSONDecoded = DecodeJSON(response.body) - if (JSONDecoded["Major"] == GetConVarString("MajorVersion") && JSONDecoded["Minor"] == GetConVarString("MinorVersion") && JSONDecoded["Patch"] == GetConVarString("PatchVersion")) { - print(prefix + currentVersion + " has been loaded and is up to date.") - } else { - print(prefix + currentVersion + " has been loaded. Recommend updating to latest version: v" + JSONDecoded["Major"] + "." + JSONDecoded["Minor"] + "." + JSONDecoded["Patch"] + ".") - } - } - void functionref(HttpRequestFailure) onFailure = void function(HttpRequestFailure failure) - { - print(prefix + currentVersion + " has been loaded and has encountered an error getting latest version data.") - } - NSHttpRequest(getVersion, onSuccess, onFailure) -} - -void function GetStatsFromToneAPI() -{ - string UID = NSGetLocalPlayerUID() - array allHttpRequests = ["/weapons", "/weapons", "/gamemodes", "/maps"] - array allHttpParameters = [UID, "!" + UID, UID, UID] - array keyValues = ["weaponsLocal", "weaponsGlobal", "gamemodes", "maps"] - for (int i; i < allHttpRequests.len(); i++) { - HttpRequest getStats - getStats.method = HttpRequestMethod.GET - getStats.url = HTTPrequestURL + allHttpRequests[i] - getStats.queryParameters["player"] <- [allHttpParameters[i]] - void functionref(HttpRequestResponse) onSuccess = void function(HttpRequestResponse response) : (i, allHttpRequests, keyValues) - { - table DecodedJSON = DecodeJSON(response.body) - table keyandvalues = {} - foreach (var key, var value in DecodedJSON) { - table values = expect table(value) - keyandvalues[string(key)] <- values - } - pulseData[keyValues[i]] <- keyandvalues - print(prefix + "has succeeded in getting stats for " + allHttpRequests[i].slice(1)) - } - void functionref(HttpRequestFailure) onFailure = void function(HttpRequestFailure failure) : (i, allHttpRequests) - { - print(prefix + "has encountered an error getting stats for " + allHttpRequests[i].slice(1)) - } - NSHttpRequest(getStats, onSuccess, onFailure) - } -} - -void function fetchGamemodeStatsFromToneAPI(string mapName) -{ - mapGamemodeEntries = 0 - string mapNameSliced = mapName.slice(3) - HttpRequest getMapGamemodeStats - getMapGamemodeStats.method = HttpRequestMethod.GET - getMapGamemodeStats.url = HTTPrequestURL + "/gamemodes" - getMapGamemodeStats.queryParameters["map"] <- [mapNameSliced] - getMapGamemodeStats.queryParameters["player"] <- [NSGetLocalPlayerUID()] - void functionref(HttpRequestResponse) onSuccess = void function(HttpRequestResponse response):(mapName) - { - table JSONForConversion = DecodeJSON(response.body) - table mapData = {} - foreach (var gamemode, var value in JSONForConversion) { - table test = expect table(value) - mapData[string(gamemode)] <- test.kills - mapGamemodeEntries += 1 - } - - globalToneAPIGamemodeMapData[mapName] <- mapData - - print(prefix + "Map gamemode data has been processed with a total of " + string(mapGamemodeEntries) + " entries.") - } - - void functionref(HttpRequestFailure) onFailure = void function(HttpRequestFailure failure) - { - print(prefix + "Encountered an error getting map gamemode data...") - } - - NSHttpRequest(getMapGamemodeStats, onSuccess, onFailure) -} - -int function getFromToneAPI(string parameter, string key, string value){ - - if (key == "maps") { - parameter = parameter.slice(3) - } - if (key in pulseData) { - table keyandvalTable = expect table(pulseData[key]) - // look to array keyValues for possible values for key - if (parameter in keyandvalTable) { - table valTable = expect table(keyandvalTable[parameter]) - return expect int(valTable[value]) - // possible values for parameter: kills, deaths, deaths_while_equipped, total_distance, max_distance - } - } - return 0 -} diff --git a/mod/scripts/vscripts/ui/menu_stats.nut b/mod/scripts/vscripts/ui/menu_stats.nut deleted file mode 100644 index 2cae746..0000000 --- a/mod/scripts/vscripts/ui/menu_stats.nut +++ /dev/null @@ -1,53 +0,0 @@ - -global function InitViewStatsMenu - -struct{ - array allMaps -} file - -void function InitViewStatsMenu() -{ - var menu = GetMenu( "ViewStatsMenu" ) - - AddMenuEventHandler( menu, eUIEvent.MENU_OPEN, OnViewStats_Open ) - - var button = Hud_GetChild( menu, "BtnOverview" ) - SetButtonRuiText( button, Localize( "#STATS_OVERVIEW" ) ) - AddButtonEventHandler( button, UIE_CLICK, AdvanceMenuEventHandler( GetMenu( "ViewStats_Overview_Menu" ) ) ) - - button = Hud_GetChild( menu, "BtnTime" ) - SetButtonRuiText( button, Localize( "#STATS_TIME" ) ) - AddButtonEventHandler( button, UIE_CLICK, AdvanceMenuEventHandler( GetMenu( "ViewStats_Time_Menu" ) ) ) - - button = Hud_GetChild( menu, "BtnPilotWeapons" ) - SetButtonRuiText( button, Localize( "#STATS_PILOT_WEAPONS" ) ) - AddButtonEventHandler( button, UIE_CLICK, AdvanceMenuEventHandler( GetMenu( "ViewStats_Weapons_Menu" ) ) ) - - button = Hud_GetChild( menu, "BtnTitans" ) - SetButtonRuiText( button, Localize( "#STATS_TITANS" ) ) - AddButtonEventHandler( button, UIE_CLICK, AdvanceMenuEventHandler( GetMenu( "ViewStats_Titans_Menu" ) ) ) - - button = Hud_GetChild( menu, "BtnMaps" ) - SetButtonRuiText( button, Localize( "#STATS_MAPS" ) ) - AddButtonEventHandler( button, UIE_CLICK, AdvanceMenuEventHandler( GetMenu( "ViewStats_Maps_Menu" ) ) ) - - button = Hud_GetChild( menu, "BtnMisc" ) - SetButtonRuiText( button, Localize( "#STATS_MISC" ) ) - AddButtonEventHandler( button, UIE_CLICK, AdvanceMenuEventHandler( GetMenu( "ViewStats_Misc_Menu" ) ) ) - - AddMenuFooterOption( menu, BUTTON_A, "#A_BUTTON_SELECT" ) - AddMenuFooterOption( menu, BUTTON_B, "#B_BUTTON_BACK", "#BACK" ) -} - -void function OnViewStats_Open() -{ - foreach(elemNum in file.allMaps){ - string mapName = expect string(file.allMaps[ elemNum ]) - fetchGamemodeStatsFromToneAPI(mapName) - } - GetStatsFromToneAPI() - UI_SetPresentationType( ePresentationType.DEFAULT ) - - //UpdateViewStatsKillsMenu() - //UpdateViewStatsDistanceMenu() -} diff --git a/mods/ToneAPI.pulse/README.md b/mods/ToneAPI.pulse/README.md new file mode 100644 index 0000000..28d57de --- /dev/null +++ b/mods/ToneAPI.pulse/README.md @@ -0,0 +1,80 @@ +# Pulse + +Pulse is a clientside mod for [TF|2 + Northstar](https://github.com/R2Northstar/Northstar) letting you view killstats collected by [the Tone API](https://toneapi.github.io/ToneAPI_webclient/) using the otherwise non-functional `Stats` tab. + +## Currently, features include: +- mostly functional `Overview` tab, +- functional `Time` tab, +- Pilot weapon data displayed in `Pilot Weapons` tab, +- Titan kills displayed in `Titans` tab, +- functional `Maps` tab, +- real-time updates. + +Want to know what we have planned for future updates? See [here.](https://github.com/ToneAPI/pulse/projects?query=is%3Aopen) + +Found a bug? Make an issue on GitHub [here.](https://github.com/ToneAPI/pulse/issues/new) + +
+Features, more in-depth: + +### `Overview` +- Most Kills - shows the weapon with most accumulated kills. +- Nemesis Weapon - shows the weapon that has killed you the most. +- Most Effective - shows weapon with the highest K/D ratio. +- Kill/Death Ratio - shows the overall K/D ratio of player compared to global K/D. +- Kills as Pilot - shows general kill data for player as Pilot, visualised in the table below: + + | Name | Description | + |--------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + | Pilots |Shows **ALL KILLS** accumulated by player as Pilot. Differentiating Pilot and Titan kills will come in a later Tone API update.| + | Titans |Non-functional, see line above.| + | AI kills |Non-functional, Tone API does not collect AI kill data.| + | Pilot melee kills |Shows only melee kills accumulated by player as Pilot.| + | Pilot executions |Shows only executions accumulated by player as Pilot.| + +- Kills as Titan - shows general kill data for player as Titan, visualised in the table below: + + | Name | Description | + |--------------------|------------------------------------------------------| + | Pilots |Shows **ALL KILLS** accumulated by player as Titan. Differentiating Pilot and Titan kills will come in a later Tone API update.| + | Titans |Non-functional, see line above.| + | Titan executions |Shows only executions accumulated by player as Titan.| + | Pilots meleed |Shows only melee kills accumulated by player as Titan.| + | Pilot roadkills |Shows only roadkills accumulated by player as Titan.| + +- Upper statbox values have been zeroed out, these don't yet work and are set to 0 to avoid confusion. + +### `Time` + +- Kills by Class shows kills by Pilot and Titan on a piechart. +- Kills by Titan shows kills by all Titan classes on a piechart. +- Kills by Gamemode shows kills on each gamemode played on a piechart. + +### `Pilot Weapons` + +- shows total accumulated kills per weapon under Total Kills. +- shows deaths with given weapon equipped under Deaths with Weapon. + +### `Titans` + +- shows total accumulated kills per Titan under Total Kills. + +### `Maps` + +- Player Map Stats - shows general data for player on chosen map, visualised in the table below: + + | Name | Description | + |-----------------------|------------------------------------------------------- | + | Kills on Map |Shows all kills accumulated by player on chosen map.| + | Deaths on Map |Shows all deaths accumulated by player on chosen map.| + | Total Shot Distance |Shows total distance of kills accumulated by player on chosen map.| + | Maximum Shot Distance |Shows highest distance kill by player on chosen map.| + +- Kills by Gamemode - piechart showing all kills (in percent) on chosen map by gamemode. +
+ +## DISCLAIMER + +The Tone API is NOT used by every server, view a list of supported servers [here.](https://tone.sleepycat.date/v2/client/servers) + +Pulse is very much a W.I.P as of right now, features may be missing/nonfunctional. diff --git a/mod.json b/mods/ToneAPI.pulse/mod.json similarity index 55% rename from mod.json rename to mods/ToneAPI.pulse/mod.json index a3c0074..9ad4981 100644 --- a/mod.json +++ b/mods/ToneAPI.pulse/mod.json @@ -1,7 +1,7 @@ { "Name": "ToneAPI.pulse", "Description": "Displays Tone API kill data in the Stats tab.", - "Version": "1.2.2", + "Version": "2.0.0", "LoadPriority": 1, "ConVars": [ { @@ -11,27 +11,19 @@ { "Name": "VersionURL", "DefaultValue": "https://raw.githubusercontent.com/ToneAPI/pulse/main/version.json" - }, - { - "Name": "MajorVersion", - "DefaultValue": "1" - }, - { - "Name": "MinorVersion", - "DefaultValue": "2" - }, - { - "Name": "PatchVersion", - "DefaultValue": "2" } ], "Scripts": [ { - "Path": "pulse.gnut", - "RunOn": "UI", - "UICallback": { - "After": "pulseInit" - } + "Path":"pulse-common-func.gnut", + "RunOn":"UI" + }, + { + "Path": "pulse.gnut", + "RunOn": "UI", + "UICallback": { + "After": "pulseInit" + } } ] } diff --git a/mod/resource/ui/menus/viewstats_maps.menu b/mods/ToneAPI.pulse/mod/resource/ui/menus/viewstats_maps.menu similarity index 100% rename from mod/resource/ui/menus/viewstats_maps.menu rename to mods/ToneAPI.pulse/mod/resource/ui/menus/viewstats_maps.menu diff --git a/mod/resource/ui/menus/viewstats_overview.menu b/mods/ToneAPI.pulse/mod/resource/ui/menus/viewstats_overview.menu similarity index 100% rename from mod/resource/ui/menus/viewstats_overview.menu rename to mods/ToneAPI.pulse/mod/resource/ui/menus/viewstats_overview.menu diff --git a/mod/resource/ui/menus/viewstats_weapons.menu b/mods/ToneAPI.pulse/mod/resource/ui/menus/viewstats_weapons.menu similarity index 100% rename from mod/resource/ui/menus/viewstats_weapons.menu rename to mods/ToneAPI.pulse/mod/resource/ui/menus/viewstats_weapons.menu diff --git a/mods/ToneAPI.pulse/mod/scripts/vscripts/pulse-common-func.gnut b/mods/ToneAPI.pulse/mod/scripts/vscripts/pulse-common-func.gnut new file mode 100644 index 0000000..ae409fb --- /dev/null +++ b/mods/ToneAPI.pulse/mod/scripts/vscripts/pulse-common-func.gnut @@ -0,0 +1,58 @@ +globalize_all_functions + + + +global string pulsePrefix +table withMapName = {} +void function pulsePrefixSet() //Sets our prefix +{ + pulsePrefix = format("\x1b[38;2;%i;%i;%im[PULSE]\x1b[0m", 255, 178, 102) +} + +void function pulsePrintt(string content) //Prints whatever we want printed with given prefix (by standard [PULSE]). +{ + printt(format("%s " + content, pulsePrefix)) +} + +bool function pulseRequestData(string givenURL, table parameterTable, string tablepath) //Gets data from API (see ToneURL within mod.json) and puts it into one table for processing. +{ + table > requestTable + pulsePrintt(tablepath) + void functionref (HttpRequestResponse) onSuccess = void function(HttpRequestResponse response) : (parameterTable, requestTable, tablepath) + { + table DecodedJSON = DecodeJSON(response.body) + if ("map" in parameterTable) + { + string mapName = expect string(parameterTable["map"]) + withMapName[mapName] <- DecodedJSON + pulseData[tablepath] <- withMapName + } else { + pulseData[tablepath] <- DecodedJSON + } + } + void functionref (HttpRequestFailure) onFailure = void function(HttpRequestFailure failure) + { + pulsePrintt("Something went wrong while getting data.") + } + foreach (key, value in parameterTable) + { + requestTable[string(key)] <- [string(value)] + } + return NSHttpGet(givenURL, requestTable, onSuccess, onFailure) +} + +int function pulseParse(...) //Returns data from our pulseData table. +{ + table parser = pulseData + int result = 0 + for (int i; i < vargc; i++) { + if (i < expect int(vargc) - 1) { + if (expect string (vargv[i]) in parser) { + parser = expect table(parser[expect string(vargv[i])]) + } else {break} + } else { + result = expect int(parser[expect string(vargv[i])]) + } + } + return result +} \ No newline at end of file diff --git a/mods/ToneAPI.pulse/mod/scripts/vscripts/pulse.gnut b/mods/ToneAPI.pulse/mod/scripts/vscripts/pulse.gnut new file mode 100644 index 0000000..a2efbe9 --- /dev/null +++ b/mods/ToneAPI.pulse/mod/scripts/vscripts/pulse.gnut @@ -0,0 +1,30 @@ +global function pulseInit +global function pulseGetData + +global table pulseData = {} + +struct { + array allMaps +} file + +void function pulseInit() +{ + pulsePrefixSet() + file.allMaps = GetPrivateMatchMaps() + pulsePrintt("Initialized.") +} + +void function pulseGetData() +{ + string URL = GetConVarString("ToneURL") + array URLpath = ["/weapons", "/weapons", "/gamemodes", "/gamemodes", "/maps"] + array tablepath = ["weaponsLocal", "weaponsGlobal", "gamemodesAll", "gamemodesSeparated", "maps"] + pulseRequestData(URL + URLpath[0], {["player"] = NSGetLocalPlayerUID()}, tablepath[0]) + pulseRequestData(URL + URLpath[1], {["player"] = "!" + NSGetLocalPlayerUID()}, tablepath[1]) + pulseRequestData(URL + URLpath[2], {["player"] = NSGetLocalPlayerUID()}, tablepath[2]) + foreach (map in file.allMaps) + { + pulseRequestData(URL + URLpath[3], {["player"] = NSGetLocalPlayerUID(), ["map"] = map.slice(3)}, tablepath[3]) + } + pulseRequestData(URL + URLpath[4], {["player"] = NSGetLocalPlayerUID()}, tablepath[4]) +} \ No newline at end of file diff --git a/mods/ToneAPI.pulse/mod/scripts/vscripts/ui/menu_lobby.nut b/mods/ToneAPI.pulse/mod/scripts/vscripts/ui/menu_lobby.nut new file mode 100644 index 0000000..cd743ed --- /dev/null +++ b/mods/ToneAPI.pulse/mod/scripts/vscripts/ui/menu_lobby.nut @@ -0,0 +1,1629 @@ +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 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() + waitthread pulseGetData() + + 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 boosts = GetAllItemsOfType( eItemTypes.BURN_METER_REWARD ) + foreach ( boost in boosts ) + { + if ( boost.hidden ) + { + ClearNewStatus( null, boost.ref ) + } + } + + UpdateCallsignElement( file.callsignCard ) + RefreshCreditsAvailable() + + bool emotesAreEnabled = EmotesEnabled() + // "Customize" + { + bool anyNewPilotItems = HasAnyNewPilotItems( player ) + bool anyNewTitanItems = HasAnyNewTitanItems( player ) + bool anyNewBoosts = HasAnyNewBoosts( player ) + bool anyNewCommsIcons = emotesAreEnabled ? HasAnyNewDpadCommsIcons( player ) : false + bool anyNewCustomizeHeader = (anyNewPilotItems || anyNewTitanItems || anyNewBoosts || anyNewCommsIcons) + + RuiSetBool( Hud_GetRui( file.customizeHeader ), "isNew", anyNewCustomizeHeader ) + ComboButton_SetNew( file.pilotButton, anyNewPilotItems ) + ComboButton_SetNew( file.titanButton, anyNewTitanItems ) + ComboButton_SetNew( file.boostsButton, anyNewBoosts ) + } + + // "Store" + { + bool storeIsNew = DLCStoreShouldBeMarkedAsNew() + RuiSetBool( Hud_GetRui( file.storeHeader ), "isNew", storeIsNew ) + ComboButton_SetNew( file.storeButton, storeIsNew ) + } + + // "Callsign" + { + bool anyNewBanners = HasAnyNewCallsignBanners( player ) + bool anyNewPatches = HasAnyNewCallsignPatches( player ) + bool anyNewFactions = HasAnyNewFactions( player ) && Lobby_IsFDMode() + bool anyNewCallsignHeader = (anyNewBanners || anyNewPatches || anyNewFactions) + + RuiSetBool( Hud_GetRui( file.callsignHeader ), "isNew", anyNewCallsignHeader ) + ComboButton_SetNew( file.bannerButton, anyNewBanners ) + ComboButton_SetNew( file.patchButton, anyNewPatches ) + ComboButton_SetNew( file.factionButton, anyNewFactions ) + } + + bool faqIsNew = !GetConVarBool( "menu_faq_viewed" ) || HaveNewPatchNotes() || HaveNewCommunityNotes() + RuiSetBool( Hud_GetRui( file.settingsHeader ), "isNew", faqIsNew ) + ComboButton_SetNew( file.faqButton, faqIsNew ) + + TryUnlockSRSCallsign() + + Lobby_UpdateInboxButtons() + + if ( file.shouldAutoOpenFDMenu ) + { + file.shouldAutoOpenFDMenu = false + AdvanceMenu( GetMenu( GetPlaylistMenuName() ) ) + AdvanceMenu( GetMenu( "FDMenu" ) ) + } + } +} + +bool function DLCStoreShouldBeMarkedAsNew() +{ + if ( !IsFullyConnected() ) + return false + + if ( !IsPersistenceAvailable() ) + return false + + bool hasSeenStore = expect bool( GetPersistentVar( "hasSeenStore" ) ) + bool result = (!hasSeenStore) + return result +} + +void function LobbyMenuUpdate( var menu ) +{ + EndSignal( uiGlobal.signalDummy, "CleanupInGameMenus" ) + + while ( GetTopNonDialogMenu() == menu ) + { + bool inPendingOpenInvite = InPendingOpenInvite() + Hud_SetLocked( file.findGameButton, !IsPartyLeader() || inPendingOpenInvite ) + Hud_SetLocked( file.inviteRoomButton, IsOpenInviteVisible() || GetPartySize() > 1 || inPendingOpenInvite ) + Hud_SetLocked( file.inviteFriendsButton, inPendingOpenInvite ) + + bool canGenUp = false + if ( GetUIPlayer() ) + canGenUp = GetPersistentVarAsInt( "xp" ) == GetMaxPlayerXP() && GetGen() < MAX_GEN + + Hud_SetVisible( file.genUpButton, canGenUp ) + Hud_SetEnabled( file.genUpButton, canGenUp ) + + WaitFrame() + } +} + +void function SetNextAutoMatchmakingPlaylist( string playlistName ) +{ + file.nextAutoMatchmakingPlaylist = playlistName +} + +string function GetNextAutoMatchmakingPlaylist() +{ + return file.nextAutoMatchmakingPlaylist +} + +void function PutPlayerInMatchmakingAfterDelay() +{ + Signal( uiGlobal.signalDummy, "PutPlayerInMatchmakingAfterDelay" ) + EndSignal( uiGlobal.signalDummy, "PutPlayerInMatchmakingAfterDelay" ) + EndSignal( uiGlobal.signalDummy, "CancelRestartingMatchmaking" ) + EndSignal( uiGlobal.signalDummy, "LeaveParty" ) + EndSignal( uiGlobal.signalDummy, "OnCloseLobbyMenu" ) + EndSignal( uiGlobal.signalDummy, "CleanupInGameMenus" ) + + if ( AreWeMatchmaking() ) //Party member, party leader is already searching + return + + entity player = GetUIPlayer() + if ( !player ) + return + + string lastPlaylist = expect string( player.GetPersistentVar( "lastPlaylist" ) ) + + //Bump player out of match making if they were playing coliseum and are out of tickets. + if ( ("coliseum" == lastPlaylist) && Player_GetColiseumTicketCount( GetLocalClientPlayer() ) <= 0 ) + { + SetNextAutoMatchmakingPlaylist( "" ) + return + } + + // Need to know if you were a party member before the countdown starts in case you leave + bool wasAPartyMemberThatIsNotLeader = AmIPartyMember() + waitthread WaitBeforeRestartingMatchmaking() + // Only the leader should proceed to start matchmaking + if ( wasAPartyMemberThatIsNotLeader ) + return + + if ( !Console_HasPermissionToPlayMultiplayer() ) + { + ClientCommand( "disconnect" ) + return + } + + string playlistToSearch = lastPlaylist + string nextAutoPlaylist = GetNextAutoMatchmakingPlaylist() + if ( nextAutoPlaylist.len() > 0 ) + playlistToSearch = nextAutoPlaylist + + StartMatchmakingPlaylists( playlistToSearch ) +} + +void function WaitBeforeRestartingMatchmaking() +{ + Signal( uiGlobal.signalDummy, "BypassWaitBeforeRestartingMatchmaking" ) + EndSignal( uiGlobal.signalDummy, "BypassWaitBeforeRestartingMatchmaking" ) + + float timeToWait + + bool isPartyMemberThatIsNotLeader = AmIPartyMember() + SetPutPlayerInMatchmakingAfterDelay( !isPartyMemberThatIsNotLeader ) + + if ( isPartyMemberThatIsNotLeader ) + timeToWait = 99999 //HACK, JFS + else + timeToWait = GetCurrentPlaylistVarFloat( "wait_before_restarting_matchmaking_time", 30.0 ) + + float timeToEnd = Time() + timeToWait + + UpdateTimeToRestartMatchmaking( timeToEnd ) + + OnThreadEnd( + function() : ( ) + { + UpdateTimeToRestartMatchmaking( 0.0 ) + UpdateFooterOptions() + } + ) + + if ( isPartyMemberThatIsNotLeader ) + { + while( Time() < timeToEnd ) //Hack hack, JFS. No appropriate signals for StartMatchmaking() being called. Replace when code gives us notifications about it + { + if ( isPartyMemberThatIsNotLeader != ( AmIPartyMember() ) ) //Party Status changed. Party leader probably left? + break + + if ( AreWeMatchmaking() ) //Need to break out if Party Leader brings us into matchmaking + break + + WaitFrame() + } + + } + else + { + wait timeToWait + } +} + +void function OnLobbyMenu_Close() +{ + Signal( uiGlobal.signalDummy, "OnCloseLobbyMenu" ) +} + +void function OnLobbyMenu_NavigateBack() +{ + if ( ChatroomIsVisibleAndFocused() ) + { + foreach ( button in file.lobbyButtons ) + { + if ( Hud_IsVisible( button ) && Hud_IsEnabled( button ) && !Hud_IsLocked( button ) ) + { + Hud_SetFocused( button ) + return + } + } + } + + if ( InPendingOpenInvite() ) + LeaveOpenInvite() + else + LeaveDialog() +} + +function GameStartTime_Changed() +{ + UpdateGameStartTimeCounter() +} + +function ShowGameSummary_Changed() +{ + if ( level.ui.showGameSummary ) + uiGlobal.EOGOpenInLobby = true +} + +function UpdateGameStartTimeCounter() +{ + if ( level.ui.gameStartTime == null ) + return + + MatchmakingSetSearchText( "#STARTING_IN_LOBBY" ) + MatchmakingSetCountdownTimer( expect float( level.ui.gameStartTime + 0.0 ), true ) + + HideMatchmakingStatusIcons() +} + +bool function MatchmakingStatusShouldShowAsActiveSearch( string matchmakingStatus ) +{ + if ( matchmakingStatus == "#MATCHMAKING_QUEUED" ) + return true + if ( matchmakingStatus == "#MATCHMAKING_ALLOCATING_SERVER" ) + return true + if ( matchmakingStatus == "#MATCHMAKING_MATCH_CONNECTING" ) + return true + + return false +} + +string function GetActiveSearchingPlaylist() +{ + if ( !IsConnected() ) + return "" + if ( !AreWeMatchmaking() ) + return "" + + string matchmakingStatus = GetMyMatchmakingStatus() + if ( !MatchmakingStatusShouldShowAsActiveSearch( matchmakingStatus ) ) + return "" + + string param1 = GetMyMatchmakingStatusParam( 1 ) + return param1 +} + +float function CalcMatchmakingWaitTime() +{ + float result = ((file.matchmakingStartTime > 0.01) ? (Time() - file.matchmakingStartTime) : 0.0) + return result +} + +float function GetMixtapeWaitTimeForPlaylist( string playlistName ) +{ + float maxWaitTime = float( GetPlaylistVarOrUseValue( playlistName, "mixtape_timeout", "0" ) ) + return maxWaitTime +} + +void function UpdateRestartMatchmakingStatus( float time ) +{ + if ( AmIPartyMember() ) + { + MatchmakingSetSearchText( "#MATCHMAKING_WAIT_ON_PARTY_LEADER_RESTARTING_MATCHMAKING" ) + } + else + { + string statusText = "#MATCHMAKING_WAIT_BEFORE_RESTARTING_MATCHMAKING" + string matchmakeNowText = "" + if ( uiGlobal.activeMenu == GetMenu( "SearchMenu" ) ) + matchmakeNowText = Localize( "#MATCHMAKING_WAIT_MATCHMAKE_NOW" ) + MatchmakingSetSearchText( statusText, matchmakeNowText ) + MatchmakingSetCountdownTimer( time, false ) + } +} + +void function UpdateMatchmakingStatus() +{ + EndSignal( uiGlobal.signalDummy, "CleanupInGameMenus" ) + + OnThreadEnd( + function() : () + { + printt( "Hiding all matchmaking elems due to UpdateMatchmakingStatus thread ending" ) + + HideMatchmakingStatusIcons() + + MatchmakingSetSearchText( "" ) + MatchmakingSetCountdownTimer( 0.0, true ) + + MatchmakingSetSearchVisible( false ) + MatchmakingSetCountdownVisible( false ) + } + ) + + MatchmakingSetSearchVisible( true ) + MatchmakingSetCountdownVisible( true ) + + var lobbyMenu = GetMenu( "LobbyMenu" ) + var searchMenu = GetMenu( "SearchMenu" ) + var postGameMenu = GetMenu( "PostGameMenu" ) + + string lastActiveSearchingPlaylist + file.matchmakingStartTime = 0.0 + file.etaTime = 0 + file.etaMaxMinutes = int( GetCurrentPlaylistVarOrUseValue( "etaMaxMinutes", file.etaMaxMinutes ) ) + file.lastMixtapeMatchmakingStatus = "" + + while ( true ) + { + int lobbyType = GetLobbyTypeScript() + string matchmakingStatus = GetMyMatchmakingStatus() + bool isConnectingToMatch = matchmakingStatus == "#MATCHMAKING_MATCH_CONNECTING" + + { + string activeSearchingPlaylist = GetActiveSearchingPlaylist() + if ( lastActiveSearchingPlaylist != activeSearchingPlaylist ) + { + if ( activeSearchingPlaylist.len() > 0 ) + { + lastActiveSearchingPlaylist = activeSearchingPlaylist + file.matchmakingStartTime = Time() + } + else + { + lastActiveSearchingPlaylist = "" + file.matchmakingStartTime = 0.0 + } + } + + if ( isConnectingToMatch && (matchmakingStatus != file.lastMixtapeMatchmakingStatus) ) + { + EmitUISound( MATCHMAKING_AUDIO_CONNECTING ) + + int mixtape_version = GetMixtapeMatchmakingVersion() + if ( IsMixtapeVersionNew() ) + LogMixtapeHasNew( mixtape_version ) + } + + file.lastMixtapeMatchmakingStatus = matchmakingStatus + } + + if ( level.ui.gameStartTime != null || lobbyType == eLobbyType.PRIVATE_MATCH ) + { + if ( level.ui.gameStartTimerComplete ) + { + MatchmakingSetSearchText( matchmakingStatus, GetMyMatchmakingStatusParam( 1 ), GetMyMatchmakingStatusParam( 2 ), GetMyMatchmakingStatusParam( 3 ), GetMyMatchmakingStatusParam( 4 ) ) + } + + if ( uiGlobal.activeMenu == searchMenu ) + CloseActiveMenu() + } + else if ( GetTimeToRestartMatchMaking() > 0 ) + { + UpdateRestartMatchmakingStatus( GetTimeToRestartMatchMaking() ) + } + else if ( level.ui.gameStartTime == null ) + { + MatchmakingSetCountdownTimer( 0.0, true ) + MatchmakingSetSearchText( "" ) + HideMatchmakingStatusIcons() + + if ( !IsConnected() || !AreWeMatchmaking() ) + { + if ( uiGlobal.activeMenu == searchMenu ) + CloseActiveMenu() + } + else + { + ShowMatchmakingStatusIcons() + + if ( GetActiveMenu() == lobbyMenu && !IsMenuInMenuStack( searchMenu ) ) + { + CloseAllDialogs() + AdvanceMenu( searchMenu ) + } + + var statusEl = Hud_GetChild( searchMenu, "MatchmakingStatusBig" ) + if ( matchmakingStatus == "#MATCH_NOTHING" ) + { + Hud_Hide( statusEl ) + } + else if ( MatchmakingStatusShouldShowAsActiveSearch( matchmakingStatus ) ) + { + string playlistName = GetMyMatchmakingStatusParam( 1 ) + int etaSeconds = int( GetMyMatchmakingStatusParam( 2 ) ) + int mapIdx = int( GetMyMatchmakingStatusParam( 3 ) ) + int modeIdx = int( GetMyMatchmakingStatusParam( 4 ) ) + string playlistList = GetMyMatchmakingStatusParam( 5 ) + + { + string statusText = Localize( "#MATCHMAKING_PLAYLISTS" ) + RuiSetString( Hud_GetRui( statusEl ), "statusText", statusText ) + for ( int idx = 1; idx <= 5; ++idx ) + RuiSetString( Hud_GetRui( statusEl ), ("bulletPointText" + idx), "" ) + + const int MAX_SHOWN_PLAYLISTS = 9 + array< string > searchingPlaylists = split( playlistList, "," ) + int searchingCount = minint( searchingPlaylists.len(), MAX_SHOWN_PLAYLISTS ) + RuiSetInt( Hud_GetRui( statusEl ), "playlistCount", searchingCount ) + for( int idx = 0; idx < searchingCount; ++idx ) + { + asset playlistThumbnail = GetPlaylistThumbnailImage( searchingPlaylists[idx] ) + RuiSetImage( Hud_GetRui( statusEl ), format( "playlistIcon%d", idx ), playlistThumbnail ) + } + } + + Hud_Show( statusEl ) + + if ( mapIdx > -1 && modeIdx > -1 ) + { + if ( file.preCacheInfo.playlistName != playlistName || file.preCacheInfo.mapIdx != mapIdx || file.preCacheInfo.modeIdx != modeIdx ) + { + file.preCacheInfo.playlistName = playlistName + file.preCacheInfo.mapIdx = mapIdx + file.preCacheInfo.modeIdx = modeIdx + } + } + + string etaStr = "" + if ( !etaSeconds && !isConnectingToMatch ) + { + matchmakingStatus = "#MATCHMAKING_SEARCHING_FOR_MATCH" + } + else + { + int now = int( Time() ) + int etaTime = now + etaSeconds + if ( !file.etaTime || etaTime < file.etaTime ) + file.etaTime = etaTime + + int etaSeconds = file.etaTime - now + if ( etaSeconds <= 0 ) + file.etaTime = etaTime + + etaSeconds = file.etaTime - now + if ( etaSeconds <= 90 ) + { + etaStr = Localize( "#MATCHMAKING_ETA_SECONDS", etaSeconds ) + } + else + { + int etaMinutes = int( ceil( etaSeconds / 60.0 ) ) + if ( etaMinutes < file.etaMaxMinutes ) + etaStr = Localize( "#MATCHMAKING_ETA_MINUTES", etaMinutes ) + else + etaStr = Localize( "#MATCHMAKING_ETA_UNKNOWN", etaMinutes ) + } + } + + MatchmakingSetSearchText( matchmakingStatus, etaStr ) + } + else + { + Hud_Show( statusEl ) + RuiSetString( Hud_GetRui( statusEl ), "statusText", "" ) + RuiSetInt( Hud_GetRui( statusEl ), "playlistCount", 0 ) + } + } + } + + WaitFrameOrUntilLevelLoaded() + } +} + +void function UpdateAnnouncementDialog() +{ + while ( IsLobby() && IsFullyConnected() ) + { + // Only safe on these menus. Not safe if these variables are true because they indicate the search menu or postgame menu are going to be opened. + if ( ( uiGlobal.activeMenu == GetMenu( "LobbyMenu" ) || uiGlobal.activeMenu == GetMenu( "PrivateLobbyMenu" ) ) && !file.putPlayerInMatchmakingAfterDelay && !uiGlobal.EOGOpenInLobby ) + { + entity player = GetUIPlayer() + + // Only initialize here, CloseAnnouncementDialog() handles setting it when closing + if ( uiGlobal.announcementVersionSeen == -1 ) + uiGlobal.announcementVersionSeen = player.GetPersistentVarAsInt( "announcementVersionSeen" ) + + int announcementVersion = GetConVarInt( "announcementVersion" ) + if ( announcementVersion > uiGlobal.announcementVersionSeen ) + { + OpenAnnouncementDialog() + } + else if ( uiGlobal.activeMenu != "AnnouncementDialog" && ShouldShowEmotesAnnouncement( player ) ) + { + OpenCommsIntroDialog() + } + } + + WaitFrame() + } +} + +bool function CurrentMenuIsPVEMenu() +{ + var topMenu = GetTopNonDialogMenu() + if ( topMenu == null ) + return false + + return (uiGlobal.menuData[topMenu].isPVEMenu) +} + +void function RefreshCreditsAvailable( int creditsOverride = -1 ) +{ + entity player = GetUIPlayer() + if ( !IsValid( player ) ) + return + if ( !IsPersistenceAvailable() ) + return + + int credits = creditsOverride >= 0 ? creditsOverride : GetAvailableCredits( GetLocalClientPlayer() ) + string pveTitle = "" + int pveCredits = 0 + bool isPVE = CurrentMenuIsPVEMenu() + if ( isPVE ) + { + TitanLoadoutDef loadout = GetCachedTitanLoadout( uiGlobal.editingLoadoutIndex ) + pveCredits = GetAvailableFDUnlockPoints( player, loadout.titanClass ) + pveTitle = GetTitanLoadoutName( loadout ) + } + + foreach ( elem in file.creditsAvailableElems ) + { + SetUIPlayerCreditsInfo( elem, credits, GetLocalClientPlayer().GetXP(), GetGen(), GetLevel(), GetNextLevel( GetLocalClientPlayer() ), isPVE, pveCredits, pveTitle ) + } +} + +void function SetUIPlayerCreditsInfo( var infoElement, int credits, int xp, int gen, int level, int nextLevel, bool isPVE, int pveCredits, string pveTitle ) +{ + var rui = Hud_GetRui( infoElement ) + RuiSetInt( rui, "credits", credits ) + RuiSetString( rui, "nameText", GetPlayerName() ) + + RuiSetBool( rui, "isPVE", isPVE ) + if ( isPVE ) + { + RuiSetInt( rui, "pveCredits", pveCredits ) + RuiSetString( rui, "pveTitle", pveTitle ) + } + + if ( xp == GetMaxPlayerXP() && gen < MAX_GEN ) + { + RuiSetString( rui, "levelText", PlayerXPDisplayGenAndLevel( gen, level ) ) + RuiSetString( rui, "nextLevelText", Localize( "#REGEN_AVAILABLE" ) ) + RuiSetInt( rui, "numLevelPips", GetXPPipsForLevel( level - 1 ) ) + RuiSetInt( rui, "filledLevelPips", GetXPPipsForLevel( level - 1 ) ) + } + else if ( xp == GetMaxPlayerXP() && gen == MAX_GEN ) + { + RuiSetString( rui, "levelText", PlayerXPDisplayGenAndLevel( gen, level ) ) + RuiSetString( rui, "nextLevelText", Localize( "#MAX_GEN" ) ) + RuiSetInt( rui, "numLevelPips", GetXPPipsForLevel( level - 1 ) ) + RuiSetInt( rui, "filledLevelPips", GetXPPipsForLevel( level - 1 ) ) + } + else + { + RuiSetString( rui, "levelText", PlayerXPDisplayGenAndLevel( gen, level ) ) + RuiSetString( rui, "nextLevelText", PlayerXPDisplayGenAndLevel( gen, nextLevel ) ) + RuiSetInt( rui, "numLevelPips", GetXPPipsForLevel( level ) ) + RuiSetInt( rui, "filledLevelPips", GetXPFilledPipsForXP( xp ) ) + } + + CallsignIcon callsignIcon = PlayerCallsignIcon_GetActive( GetLocalClientPlayer() ) + + RuiSetImage( rui, "callsignIcon", callsignIcon.image ) +} + +void function OpenServerBrowser( var button ) +{ + if ( Hud_IsLocked( button ) ) + return + + // nothing here yet lol + // look at OpenSelectedPlaylistMenu for advancing to server browser menu probably + AdvanceMenu( GetMenu( "ServerBrowserMenu" ) ) +} + +void function BigPlayButton1_Activate( var button ) +{ + if ( Hud_IsLocked( button ) ) + return + + SendOpenInvite( false ) + OpenSelectedPlaylistMenu() +} + +function EnableButton( button ) +{ + Hud_SetEnabled( button, true ) + Hud_Show( button ) +} + +function DisableButton( button ) +{ + Hud_SetEnabled( button, false ) + Hud_Hide( button ) +} + +void function OpenSelectedPlaylistMenu() +{ + if ( Lobby_IsFDMode() ) + { + AdvanceMenu( GetMenu( "FDMenu" ) ) + } + else + { + string playlistMenuName = GetPlaylistMenuName() + AdvanceMenu( GetMenu( playlistMenuName ) ) + } +} + +function UpdateLobbyUI() +{ + if ( uiGlobal.updatingLobbyUI ) + return + uiGlobal.updatingLobbyUI = true + + thread UpdateLobbyType() + thread UpdateMatchmakingStatus() + thread UpdateChatroomThread() + //thread UpdateInviteJoinButton() + thread UpdateInviteFriendsToNetworkButton() + thread UpdatePlayerInfo() + + if ( uiGlobal.menuToOpenFromPromoButton != null ) + { + // Special case because this menu needs a few properties set before opening + + if ( IsStoreMenu( uiGlobal.menuToOpenFromPromoButton ) ) + { + string menuName = expect string( uiGlobal.menuToOpenFromPromoButton._name ) + + void functionref() preOpenfunc = null + if ( uiGlobal.menuToOpenFromPromoButton == GetMenu( "StoreMenu_WeaponSkins" ) ) // Hardcoded special case for now + preOpenfunc = DefaultToDLC11WeaponWarpaintBundle + + OpenStoreMenu( [ menuName ], preOpenfunc ) + } + else + { + AdvanceMenu( uiGlobal.menuToOpenFromPromoButton ) + } + + uiGlobal.menuToOpenFromPromoButton = null + } + else if ( uiGlobal.EOGOpenInLobby ) + { + EOGOpen() + } + + WaitSignal( uiGlobal.signalDummy, "CleanupInGameMenus" ) + uiGlobal.updatingLobbyUI = false +} + +void function UpdateInviteJoinButton() +{ + EndSignal( uiGlobal.signalDummy, "CleanupInGameMenus" ) + var menu = GetMenu( "LobbyMenu" ) + + while ( true ) + { + if ( DoesCurrentCommunitySupportInvites() ) + ComboButton_SetText( file.inviteRoomButton, Localize( "#MENU_TITLE_INVITE_ROOM" ) ) + else + ComboButton_SetText( file.inviteRoomButton, Localize( "#MENU_TITLE_JOIN_NETWORK" ) ) + + WaitFrame() + } +} + +void function UpdateInviteFriendsToNetworkButton() +{ + EndSignal( uiGlobal.signalDummy, "CleanupInGameMenus" ) + var menu = GetMenu( "LobbyMenu" ) + + while ( true ) + { + bool areInvitesToNetworkNotAllowed = !DoesCurrentCommunitySupportChat() + if ( areInvitesToNetworkNotAllowed || ( IsCurrentCommunityInviteOnly() && !AreWeAdminInCurrentCommunity() ) ) + DisableButton( file.inviteFriendsToNetworkButton ) + else + EnableButton( file.inviteFriendsToNetworkButton ) + + WaitFrame() + } +} + +function UpdateLobbyType() +{ + EndSignal( uiGlobal.signalDummy, "CleanupInGameMenus" ) + + var menu = GetMenu( "LobbyMenu" ) + int lobbyType + local lastType + local partySize + local lastPartySize + local debugArray = [ "SOLO", "PARTY_LEADER", "PARTY_MEMBER", "MATCH", "PRIVATE_MATCH" ] // Must match enum + + WaitFrameOrUntilLevelLoaded() + + while ( true ) + { + lobbyType = GetLobbyTypeScript() + partySize = GetPartySize() + + if ( IsConnected() && ((lobbyType != lastType) || (partySize != lastPartySize)) ) + { + if ( lastType == null ) + printt( "Lobby lobbyType changing from:", lastType, "to:", debugArray[lobbyType] ) + else + printt( "Lobby lobbyType changing from:", debugArray[lastType], "to:", debugArray[lobbyType] ) + + local animation = null + + switch ( lobbyType ) + { + case eLobbyType.SOLO: + animation = "SoloLobby" + break + + case eLobbyType.PARTY_LEADER: + animation = "PartyLeaderLobby" + break + + case eLobbyType.PARTY_MEMBER: + animation = "PartyMemberLobby" + break + + case eLobbyType.MATCH: + animation = "MatchLobby" + break + + case eLobbyType.PRIVATE_MATCH: + animation = "PrivateMatchLobby" + break + } + + if ( animation != null ) + { + menu.RunAnimationScript( animation ) + } + + // Force the animation scripts (which have zero duration) to complete before anything can cancel them. + ForceUpdateHUDAnimations() + + lastType = lobbyType + lastPartySize = partySize + } + + WaitFrameOrUntilLevelLoaded() + } +} + +void function UICodeCallback_CommunityUpdated() +{ + Community_CommunityUpdated() + UpdateChatroomUI() +} + +void function UICodeCallback_FactionUpdated() +{ + printt( "Faction changed! to " + GetCurrentFaction() ); +} + +void function UICodeCallback_SetupPlayerListGenElements( table params, int gen, int rank, bool isPlayingRanked, int pilotClassIndex ) +{ + params.image = "" + params.label = "" + params.imageOverlay = "" +} + +float function GetTimeToRestartMatchMaking() +{ + return file.timeToRestartMatchMaking +} + +void function UpdateTimeToRestartMatchmaking( float time )//JFS: This uses UI time instead of server time, which leads to awkwardness in MatchmakingSetCountdownTimer() and the rui involved +{ + file.timeToRestartMatchMaking = time + + if ( time > 0 ) + { + UpdateRestartMatchmakingStatus( time ) + ShowMatchmakingStatusIcons() + } + else + { + MatchmakingSetSearchText( "" ) + MatchmakingSetCountdownTimer( 0.0, true ) + HideMatchmakingStatusIcons() + } +} + +void function HideMatchmakingStatusIcons() +{ + foreach ( element in file.searchIconElems ) + Hud_Hide( element ) + + foreach ( element in file.matchStatusRuis ) + RuiSetBool( Hud_GetRui( element ), "iconVisible", false ) +} + +void function ShowMatchmakingStatusIcons() +{ + //foreach ( element in file.searchIconElems ) + // Hud_Show( element ) + + foreach ( element in file.matchStatusRuis ) + RuiSetBool( Hud_GetRui( element ), "iconVisible", true ) +} + +void function MatchmakingSetSearchVisible( bool state ) +{ + foreach ( el in file.searchTextElems ) + { + //if ( state ) + // Hud_Show( el ) + //else + Hud_Hide( el ) + } + + foreach ( element in file.matchStatusRuis ) + RuiSetBool( Hud_GetRui( element ), "statusVisible", state ) +} + +void function MatchmakingSetSearchText( string searchText, var param1 = "", var param2 = "", var param3 = "", var param4 = "" ) +{ + foreach ( el in file.searchTextElems ) + { + Hud_SetText( el, searchText, param1, param2, param3, param4 ) + } + + foreach ( element in file.matchStatusRuis ) + { + RuiSetBool( Hud_GetRui( element ), "statusHasText", searchText != "" ) + + RuiSetString( Hud_GetRui( element ), "statusText", Localize( searchText, param1, param2, param3, param4 ) ) + } +} + + +void function MatchmakingSetCountdownVisible( bool state ) +{ + foreach ( el in file.matchStartCountdownElems ) + { + //if ( state ) + // Hud_Show( el ) + //else + Hud_Hide( el ) + } + + foreach ( element in file.matchStatusRuis ) + RuiSetBool( Hud_GetRui( element ), "timerVisible", state ) +} + +void function MatchmakingSetCountdownTimer( float time, bool useServerTime = true ) //JFS: useServerTime bool is awkward, comes from level.ui.gameStartTime using server time and UpdateTimeToRestartMatchmaking() uses UI time. +{ + foreach ( element in file.matchStatusRuis ) + { + RuiSetBool( Hud_GetRui( element ), "timerHasText", time != 0.0 ) + RuiSetGameTime( Hud_GetRui( element ), "startTime", Time() ) + RuiSetBool( Hud_GetRui( element ), "useServerTime", useServerTime ) + RuiSetGameTime( Hud_GetRui( element ), "timerEndTime", time ) + } +} + +void function OnLobbyLevelInit() +{ + UpdateCallsignElement( file.callsignCard ) + RefreshCreditsAvailable() +} + +function UpdatePlayerInfo() +{ + EndSignal( uiGlobal.signalDummy, "CleanupInGameMenus" ) + + var menu = GetMenu( "LobbyMenu" ) + + WaitFrameOrUntilLevelLoaded() + + while ( true ) + { + RefreshCreditsAvailable() + WaitFrame() + } +} + +void function TryUnlockSRSCallsign() +{ + if ( Script_IsRunningTrialVersion() ) + return + + int numCallsignsToUnlock = 0 + + if ( GetTotalLionsCollected() >= GetTotalLionsInGame() ) + numCallsignsToUnlock = 3 + else if ( GetTotalLionsCollected() >= ACHIEVEMENT_COLLECTIBLES_2_COUNT ) + numCallsignsToUnlock = 2 + else if ( GetTotalLionsCollected() >= ACHIEVEMENT_COLLECTIBLES_1_COUNT ) + numCallsignsToUnlock = 1 + else + numCallsignsToUnlock = 0 + + if ( numCallsignsToUnlock > 0 ) + ClientCommand( "UnlockSRSCallsign " + numCallsignsToUnlock ) +} + +void function SetPutPlayerInMatchmakingAfterDelay( bool value ) +{ + file.putPlayerInMatchmakingAfterDelay = value +} + +void function OnStoreButton_Activate( var button ) +{ + LaunchGamePurchaseOrDLCStore() +} + +void function OnStoreNewReleasesButton_Activate( var button ) +{ + //LaunchGamePurchaseOrDLCStore( [ "StoreMenu", "StoreMenu_NewReleases" ] ) + LaunchGamePurchaseOrDLCStore( [ "StoreMenu", "StoreMenu_WeaponSkins" ] ) +} + +void function OnStoreBundlesButton_Activate( var button ) +{ + LaunchGamePurchaseOrDLCStore( [ "StoreMenu", "StoreMenu_Sales" ] ) +} + +void function OnDpadCommsButton_Activate( var button ) +{ + AdvanceMenu( GetMenu( "EditDpadCommsMenu" ) ) +} + +void function OpenCommsIntroDialog() +{ + DialogData dialogData + dialogData.menu = GetMenu( "AnnouncementDialog" ) + dialogData.header = "#DPAD_COMMS_ANNOUNCEMENT_HEADER" + dialogData.ruiMessage.message = "#DPAD_COMMS_ANNOUNCEMENT" + dialogData.image = $"ui/menu/common/dialog_announcement_1" + + AddDialogButton( dialogData, "#DPAD_COMMS_ANNOUNCEMENT_B1" , OpenDpadCommsMenu ) + AddDialogButton( dialogData, "#DPAD_COMMS_ANNOUNCEMENT_B2" ) + + AddDialogPCBackButton( dialogData ) + AddDialogFooter( dialogData, "#A_BUTTON_ACCEPT" ) + AddDialogFooter( dialogData, "#B_BUTTON_BACK" ) + + OpenDialog( dialogData ) + + ClientCommand( "SetCommsIntroSeen" ) +} + +void function OpenDpadCommsMenu() +{ + OnDpadCommsButton_Activate( null ) +} + +bool function ShouldShowEmotesAnnouncement( entity player ) +{ + if ( !EmotesEnabled() ) + return false + + if ( player.GetPersistentVarAsInt( "numTimesUsedComms" ) > 2 ) + return false + + if ( player.GetPersistentVar( "hasBeenIntroducedToComms" ) ) + return false + + #if !DEV + if ( PlayerGetRawLevel( player ) <= 2 ) + return false + #endif + + return true +} + +void function Lobby_SetFDMode( bool mode ) +{ + file.isFDMode = mode +} + +//Function returns whether lobby is currently in "Frontier Defense" lobby mode. +bool function Lobby_IsFDMode() +{ + return file.isFDMode +} + +void function Lobby_SetAutoFDOpen( bool autoFD ) +{ + Lobby_SetFDMode( autoFD ) + file.shouldAutoOpenFDMenu = autoFD +} + +void function Lobby_SetFDModeBasedOnSearching( string playlistToSearch ) +{ + array< string > searchingPlaylists = split( playlistToSearch, "," ) + + bool isFDMode = false + int searchingCount = searchingPlaylists.len() + for( int idx = 0; idx < searchingCount; ++idx ) + { + isFDMode = isFDMode || IsFDMode( searchingPlaylists[idx] ) + if ( isFDMode ) + break + } + + Lobby_SetFDMode( isFDMode ) +} \ No newline at end of file diff --git a/mod/scripts/vscripts/ui/menu_stats_maps.nut b/mods/ToneAPI.pulse/mod/scripts/vscripts/ui/menu_stats_maps.nut similarity index 93% rename from mod/scripts/vscripts/ui/menu_stats_maps.nut rename to mods/ToneAPI.pulse/mod/scripts/vscripts/ui/menu_stats_maps.nut index 49d72b4..f5bfa07 100644 --- a/mod/scripts/vscripts/ui/menu_stats_maps.nut +++ b/mods/ToneAPI.pulse/mod/scripts/vscripts/ui/menu_stats_maps.nut @@ -140,8 +140,6 @@ void function UpdateStatsForMap( string mapName ) Hud_SetText( Hud_GetChild( file.menu, "WeaponName" ), GetMapDisplayName( mapName ) ) - fetchGamemodeStatsFromToneAPI(mapName) - // Image var imageElem = Hud_GetRui( Hud_GetChild( file.menu, "WeaponImageLarge" ) ) RuiSetImage( imageElem, "basicImage", GetMapImageForMapName( mapName ) ) @@ -155,16 +153,16 @@ void function UpdateStatsForMap( string mapName ) //SetStatBoxDisplay( Hud_GetChild( file.menu, "Stat3" ), Localize( "#STATS_GAMES_PLAYED" ), gamesPlayed ) SetStatsLabelValue( file.menu, "KillsLabel0", "KILLS ON MAP" ) - SetStatsLabelValue( file.menu, "KillsValue0", getFromToneAPI(mapName, "maps", "kills") ) + SetStatsLabelValue( file.menu, "KillsValue0", pulseParse("maps", mapName.slice(3), "kills") ) SetStatsLabelValue( file.menu, "KillsLabel1", "DEATHS ON MAP" ) - SetStatsLabelValue( file.menu, "KillsValue1", getFromToneAPI(mapName, "maps", "deaths") ) + SetStatsLabelValue( file.menu, "KillsValue1", pulseParse("maps", mapName.slice(3), "deaths") ) SetStatsLabelValue( file.menu, "KillsLabel2", "TOTAL SHOT DISTANCE" ) - SetStatsLabelValue( file.menu, "KillsValue2", string(int((1.905 * float(getFromToneAPI(mapName, "maps", "total_distance") ) ) )/100) + "m" ) + SetStatsLabelValue( file.menu, "KillsValue2", string(int((1.905 * float(pulseParse("maps", mapName.slice(3), "total_distance") ) ) )/100) + "m" ) SetStatsLabelValue( file.menu, "KillsLabel3", "MAXIMUM SHOT DISTANCE" ) - SetStatsLabelValue( file.menu, "KillsValue3", string(int((1.905 * float(getFromToneAPI(mapName, "maps", "max_distance") ) ) )/100) + "m" ) + SetStatsLabelValue( file.menu, "KillsValue3", string(int((1.905 * float(pulseParse("maps", mapName.slice(3), "max_distance") ) ) )/100) + "m" ) SetStatsLabelValue( file.menu, "KillsLabel4", "--" ) SetStatsLabelValue( file.menu, "KillsValue4", "--" ) @@ -193,10 +191,10 @@ void function UpdateStatsForMap( string mapName ) for ( int modeId = 0; modeId < enumCount; modeId++ ) { string modeName = PersistenceGetEnumItemNameForIndex( "gameModes", modeId ) - if ( mapName in globalToneAPIGamemodeMapData && modeName in globalToneAPIGamemodeMapData[mapName] ) + if (mapName.slice(3) in pulseData["gamemodesSeparated"]) { float modePlayedTime = 0 - modePlayedTime = float(globalToneAPIGamemodeMapData[mapName][modeName]) + modePlayedTime = float(pulseParse("gamemodesSeparated", mapName.slice(3), modeName, "kills")) if ( modePlayedTime > 0 ) { AddPieChartEntry( modes, GameMode_GetName( modeName ), modePlayedTime, GetGameModeDisplayColor( modeName ) ) } @@ -204,10 +202,10 @@ void function UpdateStatsForMap( string mapName ) } foreach (string key, array value in customGamemodeList) { - if ( mapName in globalToneAPIGamemodeMapData && key in globalToneAPIGamemodeMapData[mapName]) + if (mapName.slice(3) in pulseData["gamemodesSeparated"]) { float modePlayedTime = 0 - modePlayedTime = float(globalToneAPIGamemodeMapData[mapName][key]) + modePlayedTime = float(pulseParse("gamemodesSeparated", mapName.slice(3), key, "kills")) if ( modePlayedTime > 0 ) { AddPieChartEntry( modes, customGamemodeNames[key], modePlayedTime, value) } diff --git a/mod/scripts/vscripts/ui/menu_stats_overview.nut b/mods/ToneAPI.pulse/mod/scripts/vscripts/ui/menu_stats_overview.nut similarity index 94% rename from mod/scripts/vscripts/ui/menu_stats_overview.nut rename to mods/ToneAPI.pulse/mod/scripts/vscripts/ui/menu_stats_overview.nut index 4d3d9d8..b8db39e 100644 --- a/mod/scripts/vscripts/ui/menu_stats_overview.nut +++ b/mods/ToneAPI.pulse/mod/scripts/vscripts/ui/menu_stats_overview.nut @@ -52,7 +52,7 @@ void function GetTitanKills( string titanName ) foreach ( weaponRef in file.titanStatLoadout[ titanName ]) { - titanKillData[weaponRef] <- getFromToneAPI(weaponRef, "weaponsLocal", "kills") + titanKillData[weaponRef] <- pulseParse("weaponsLocal", weaponRef, "kills") } } @@ -332,7 +332,7 @@ function UpdateViewStatsOverviewMenu() if (key in titanKillData) { continue } else { - killsAsPilot += getFromToneAPI(string(key), "weaponsLocal", "kills") + killsAsPilot += pulseParse("weaponsLocal", string(key), "kills") } } @@ -407,27 +407,27 @@ function UpdateViewStatsOverviewMenu() Hud_SetText( GetElem( file.menu, "KillsAsPilotValue0" ), string( killsAsPilot ) ) Hud_SetText( GetElem( file.menu, "KillsAsPilotValue1" ), string( GetPlayerStatInt( player, "kills_stats", "titanKillsAsPilot" ) ) ) Hud_SetText( GetElem( file.menu, "KillsAsPilotValue2" ), string( GetPlayerStatInt( player, "kills_stats", "totalNPC" ) ) ) - Hud_SetText( GetElem( file.menu, "KillsAsPilotValue3" ), string( getFromToneAPI("pilot_emptyhanded", "weaponsLocal", "kills") ) ) - Hud_SetText( GetElem( file.menu, "KillsAsPilotValue4" ), string( getFromToneAPI("human_execution", "weaponsLocal", "kills") ) ) + Hud_SetText( GetElem( file.menu, "KillsAsPilotValue3" ), string( pulseParse("weaponsLocal", "pilot_emptyhanded", "kills") ) ) + Hud_SetText( GetElem( file.menu, "KillsAsPilotValue4" ), string( pulseParse("weaponsLocal", "human_execution", "kills") ) ) // Hud_SetText( GetElem( file.menu, "KillsAsPilotValue5" ), string( GetPlayerStatInt( player, "kills_stats", "titanFallKill" ) ) ) var titanMeleeKills = 0 - titanMeleeKills += getFromToneAPI("titan_punch_ion", "weaponsLocal", "kills") - titanMeleeKills += getFromToneAPI("titan_punch_scorch", "weaponsLocal", "kills") - titanMeleeKills += getFromToneAPI("titan_punch_northstar", "weaponsLocal", "kills") - titanMeleeKills += getFromToneAPI("titan_sword", "weaponsLocal", "kills") - titanMeleeKills += getFromToneAPI("titan_punch_tone", "weaponsLocal", "kills") - titanMeleeKills += getFromToneAPI("titan_punch_legion", "weaponsLocal", "kills") - titanMeleeKills += getFromToneAPI("titan_punch_vanguard", "weaponsLocal", "kills") - titanMeleeKills += getFromToneAPI("auto_titan_melee", "weaponsLocal", "kills") + titanMeleeKills += pulseParse("weaponsLocal", "titan_punch_ion", "kills") + titanMeleeKills += pulseParse("weaponsLocal", "titan_punch_scorch", "kills") + titanMeleeKills += pulseParse("weaponsLocal", "titan_punch_northstar", "kills") + titanMeleeKills += pulseParse("weaponsLocal", "titan_sword", "kills") + titanMeleeKills += pulseParse("weaponsLocal", "titan_punch_tone", "kills") + titanMeleeKills += pulseParse("weaponsLocal", "titan_punch_legion", "kills") + titanMeleeKills += pulseParse("weaponsLocal", "titan_punch_vanguard", "kills") + titanMeleeKills += pulseParse("weaponsLocal", "auto_titan_melee", "kills") - var titanExecutions = getFromToneAPI("titan_execution", "weaponsLocal", "kills") + var titanExecutions = pulseParse("weaponsLocal", "titan_execution", "kills") Hud_SetText( GetElem( file.menu, "KillsAsTitanValue0" ), string( killsAsTitan ) ) Hud_SetText( GetElem( file.menu, "KillsAsTitanValue1" ), string( GetPlayerStatInt( player, "kills_stats", "titanKillsAsTitan" ) ) ) Hud_SetText( GetElem( file.menu, "KillsAsTitanValue2" ), string( titanExecutions ) ) Hud_SetText( GetElem( file.menu, "KillsAsTitanValue3" ), string( titanMeleeKills ) ) - Hud_SetText( GetElem( file.menu, "KillsAsTitanValue4" ), string( getFromToneAPI("damagedef_titan_step", "weaponsLocal", "kills") ) ) + Hud_SetText( GetElem( file.menu, "KillsAsTitanValue4" ), string( pulseParse("weaponsLocal", "damagedef_titan_step", "kills") ) ) } function PlotKDPointsOnGraph( menu, graphIndex, values, dottedAverage ) diff --git a/mod/scripts/vscripts/ui/menu_stats_time.nut b/mods/ToneAPI.pulse/mod/scripts/vscripts/ui/menu_stats_time.nut similarity index 95% rename from mod/scripts/vscripts/ui/menu_stats_time.nut rename to mods/ToneAPI.pulse/mod/scripts/vscripts/ui/menu_stats_time.nut index 3030481..6a240ee 100644 --- a/mod/scripts/vscripts/ui/menu_stats_time.nut +++ b/mods/ToneAPI.pulse/mod/scripts/vscripts/ui/menu_stats_time.nut @@ -49,7 +49,7 @@ float function GetTitanKills( string titanName) float weaponKills = 0 foreach ( weaponRef in file.titanStatLoadout[ titanName ]) { - weaponKills += float(getFromToneAPI(weaponRef, "weaponsLocal", "kills")) + weaponKills += float(pulseParse("weaponsLocal", weaponRef, "kills")) } return weaponKills } @@ -160,10 +160,10 @@ void function UpdateViewStatsTimeMenu() for ( int modeId = 0; modeId < enumCount; modeId++ ) { string modeName = PersistenceGetEnumItemNameForIndex( "gameModes", modeId ) - if ( getFromToneAPI(modeName, "gamemodes", "kills") != 0 ) + if ( pulseParse("gamemodesAll", modeName, "kills") != 0 ) { float modePlayedTime = 0 - modePlayedTime = float(getFromToneAPI(modeName, "gamemodes", "kills")) + modePlayedTime = float(pulseParse("gamemodesAll", modeName, "kills")) if ( modePlayedTime > 0 ) { AddPieChartEntry( modes, GameMode_GetName( modeName ), modePlayedTime, GetGameModeDisplayColor( modeName ) ) } @@ -171,10 +171,10 @@ void function UpdateViewStatsTimeMenu() } foreach (string key, array value in customGamemodeList) { - if ( getFromToneAPI(key, "gamemodes", "kills") != 0 ) + if ( pulseParse("gamemodesAll", key, "kills") != 0 ) { float modePlayedTime = 0 - modePlayedTime = float(getFromToneAPI(key, "gamemodes", "kills")) + modePlayedTime = float(pulseParse("gamemodesAll", key, "kills")) if ( modePlayedTime > 0 ) { AddPieChartEntry( modes, customGamemodeNames[key], modePlayedTime, value) } diff --git a/mod/scripts/vscripts/ui/menu_stats_titans.nut b/mods/ToneAPI.pulse/mod/scripts/vscripts/ui/menu_stats_titans.nut similarity index 99% rename from mod/scripts/vscripts/ui/menu_stats_titans.nut rename to mods/ToneAPI.pulse/mod/scripts/vscripts/ui/menu_stats_titans.nut index e9cd6f8..da87817 100644 --- a/mod/scripts/vscripts/ui/menu_stats_titans.nut +++ b/mods/ToneAPI.pulse/mod/scripts/vscripts/ui/menu_stats_titans.nut @@ -175,7 +175,7 @@ void function UpdateStatsForTitan( string titanRef, int loadoutIndex ) int aiKills foreach ( weaponRef in file.titanStatLoadout[ titanRef ] ) { - totalKills += getFromToneAPI(weaponRef, "weaponsLocal", "kills") + totalKills += pulseParse("weaponsLocal", weaponRef, "kills") pilotKills += GetPlayerStatInt( player, "weapon_kill_stats", "pilots", weaponRef ) titanKills += GetPlayerStatInt( player, "weapon_kill_stats", "titansTotal", weaponRef ) aiKills += GetPlayerStatInt( player, "weapon_kill_stats", "ai", weaponRef ) diff --git a/mod/scripts/vscripts/ui/menu_stats_utility.nut b/mods/ToneAPI.pulse/mod/scripts/vscripts/ui/menu_stats_utility.nut similarity index 97% rename from mod/scripts/vscripts/ui/menu_stats_utility.nut rename to mods/ToneAPI.pulse/mod/scripts/vscripts/ui/menu_stats_utility.nut index 29be870..bc22ea6 100644 --- a/mod/scripts/vscripts/ui/menu_stats_utility.nut +++ b/mods/ToneAPI.pulse/mod/scripts/vscripts/ui/menu_stats_utility.nut @@ -338,7 +338,7 @@ table function GetOverviewWeaponData() if ( !PersistenceEnumValueIsValid( "loadoutWeaponsAndAbilities", weaponName ) ) continue - int val = getFromToneAPI(weaponName, "weaponsLocal", "kills") + int val = pulseParse("weaponsLocal", weaponName, "kills") if ( val > Table[ "most_kills" ].val ) { Table[ "most_kills" ].ref = weaponName @@ -346,7 +346,7 @@ table function GetOverviewWeaponData() Table[ "most_kills" ].val = val } - int nval = getFromToneAPI(weaponName, "weaponsLocal", "deaths") + int nval = pulseParse("weaponsLocal", weaponName, "deaths") if ( nval > Table[ "nemesis_weapon" ].val ) { Table[ "nemesis_weapon" ].ref = weaponName @@ -357,9 +357,9 @@ table function GetOverviewWeaponData() float kdval = 0 float kval = 0 float dval = 0 - if( getFromToneAPI(weaponName, "weaponsLocal", "kills") != 0 && getFromToneAPI(weaponName, "weaponsLocal", "deaths_while_equipped") != 0){ - kval = float(getFromToneAPI(weaponName, "weaponsLocal", "kills")) - dval = float(getFromToneAPI(weaponName, "weaponsLocal", "deaths_while_equipped")) + if( pulseParse("weaponsLocal", weaponName, "kills") != 0 && pulseParse("weaponsLocal", weaponName, "deaths_while_equipped") != 0){ + kval = float(pulseParse("weaponsLocal", weaponName, "kills")) + dval = float(pulseParse("weaponsLocal", weaponName, "deaths_while_equipped")) if (kval / dval > kdval){ kdval = kval / dval } diff --git a/mod/scripts/vscripts/ui/menu_stats_weapons.nut b/mods/ToneAPI.pulse/mod/scripts/vscripts/ui/menu_stats_weapons.nut similarity index 94% rename from mod/scripts/vscripts/ui/menu_stats_weapons.nut rename to mods/ToneAPI.pulse/mod/scripts/vscripts/ui/menu_stats_weapons.nut index 6fcc252..6340a5a 100644 --- a/mod/scripts/vscripts/ui/menu_stats_weapons.nut +++ b/mods/ToneAPI.pulse/mod/scripts/vscripts/ui/menu_stats_weapons.nut @@ -180,8 +180,8 @@ void function UpdateStatsForWeapon( string weaponRef ) } // Total Kills Stats - SetStatsLabelValue( file.menu, "KillsValue0", getFromToneAPI(weaponRef, "weaponsLocal", "kills") ) //Total kills - SetStatsLabelValue( file.menu, "KillsValue1", getFromToneAPI(weaponRef, "weaponsLocal", "kills") ) //Pilots + SetStatsLabelValue( file.menu, "KillsValue0", pulseParse("weaponsLocal", weaponRef, "kills") ) //Total kills + SetStatsLabelValue( file.menu, "KillsValue1", pulseParse("weaponsLocal", weaponRef, "kills") ) //Pilots SetStatsLabelValue( file.menu, "KillsValue2", GetPlayerStatInt( player, "weapon_kill_stats", "titansTotal", weaponRef ) ) //Titans - SetStatsLabelValue( file.menu, "KillsValue3", getFromToneAPI(weaponRef, "weaponsLocal", "deaths_while_equipped")) // Deaths with weapon + SetStatsLabelValue( file.menu, "KillsValue3", pulseParse("weaponsLocal", weaponRef, "deaths_while_equipped")) // Deaths with weapon } diff --git a/version.json b/version.json deleted file mode 100644 index 1d6e638..0000000 --- a/version.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "Major": "1", - "Minor": "2", - "Patch": "1" -} \ No newline at end of file