diff options
20 files changed, 7659 insertions, 197 deletions
diff --git a/Northstar.Client/mod.json b/Northstar.Client/mod.json index ac1e0951..4842755b 100644 --- a/Northstar.Client/mod.json +++ b/Northstar.Client/mod.json @@ -3,7 +3,28 @@ "Description" : "Various ui and client changes to fix bugs and add better support for mods", "Version": "1.0.0", "LoadPriority": 0, - + "ConVars": [ + { + "Name": "filter_hide_empty", + "DefaultValue": "0" + }, + { + "Name": "filter_hide_full", + "DefaultValue": "0" + }, + { + "Name": "filter_hide_protected", + "DefaultValue": "0" + }, + { + "Name": "filter_map", + "DefaultValue": "0" + }, + { + "Name": "filter_gamemode", + "DefaultValue": "0" + } + ], // ui inits need to happen before so our init callbacks get called "Scripts": [ { diff --git a/Northstar.Client/mod/resource/northstar_client_localisation_english.txt b/Northstar.Client/mod/resource/northstar_client_localisation_english.txt Binary files differindex a0a8026e..b37afef3 100644 --- a/Northstar.Client/mod/resource/northstar_client_localisation_english.txt +++ b/Northstar.Client/mod/resource/northstar_client_localisation_english.txt diff --git a/Northstar.Client/mod/resource/northstar_client_localisation_german.txt b/Northstar.Client/mod/resource/northstar_client_localisation_german.txt new file mode 100644 index 00000000..fdea511e --- /dev/null +++ b/Northstar.Client/mod/resource/northstar_client_localisation_german.txt @@ -0,0 +1,216 @@ +"lang"
+{
+ "Language" "german"
+ "Tokens"
+ {
+ "MENU_LAUNCH_NORTHSTAR" "Starte Northstar"
+ "MENU_TITLE_MODS" "Mods"
+ "RELOAD_MODS" "Mods neu laden"
+
+ "DIALOG_TITLE_INSTALLED_NORTHSTAR" "Danke, dass du Northstar installiert hast!"
+ "AUTHENTICATION_AGREEMENT_DIALOG_TEXT" "Damit Northstar funktionieren kann, muss es mithilfe des Northstar Masterservers authentifizieren. Dies setzt ein Weitergeben deines Origin Tokens an den Masterserver voraus, er wird nicht gespeichert oder für andere Zwecke verwendet.
+Drücke Ja, um zuzustimmen. Du kannst diese Entscheidung jederzeit im Modmenü ändern."
+ "BACK_AUTHENTICATION_AGREEMENT" "Authentifizierungs-Einwilligung"
+ "AUTHENTICATION_AGREEMENT" "Authentifizierungs-Einwilligung"
+ "AUTHENTICATION_AGREEMENT_RESTART" "Ein Neustart ist notwendig, um diese Änderung zu übernehmen"
+
+ "MENU_TITLE_SERVER_BROWSER" "Server Browser"
+ "NS_SERVERBROWSER_NOSERVERS" "Keine Server gefunden"
+ "NS_SERVERBROWSER_WAITINGFORSERVERS" "Warte auf Server..."
+ "NS_SERVERBROWSER_CONNECTIONFAILED" "Verbindung fehlgeschlagen!"
+ "REFRESH_SERVERS" "Neu laden"
+
+ "MENU_TITLE_CONNECT_PASSWORD" "Verbinden mit Passwort"
+ "MENU_CONNECT_MENU_CONNECT" "Verbinden"
+
+ "PRIVATE_MATCH_PAGE_PREV" "Vorherige Seite"
+ "PRIVATE_MATCH_PAGE_NEXT" "Nächste Seite"
+
+ "MENU_MATCH_SETTINGS" "Match Einstellungen"
+ "MENU_MATCH_SETTINGS_SUBMENU" "%s1 Einstellungen"
+
+ "PRIVATE_MATCH_SINGLEPLAYER_LEVEL" "%s1 (Einzelspieler)"
+
+ // fra hint for private match menu, because fra only has PL_fra_desc in vanilla
+ "PL_fra_hint" "Du bist alleine. Töte Piloten, um zu siegen. Sammle 3 Batterien für einen Titanfall."
+
+ // mode settings
+ "MODE_SETTING_CATEGORY_PILOT" "Pilot"
+ "MODE_SETTING_CATEGORY_TITAN" "Titan"
+ "MODE_SETTING_CATEGORY_RIFF" "Extras"
+ "MODE_SETTING_CATEGORY_MATCH" "Match"
+
+ "classic_mp" "Klassischer Multiplayer"
+ "run_epilogue" "Epilog"
+ "scorelimit" "Höchstpunktzahl"
+ "roundscorelimit" "Höchstpunktzahl (rundenbasiert)"
+ "timelimit" "Zeitlimit"
+ "roundtimelimit" "Zeitlimit (rundenbasiert)"
+
+ "pilot_health_multiplier" "HP Multiplikator"
+ "respawn_delay" "Respawn-Verzögerung"
+ "boosts_enabled" "Boosts"
+ "earn_meter_pilot_overdrive" "Boost-Meter overdrive"
+ "earn_meter_pilot_multiplier" "Piloten Boost-Meter Multiplikator"
+
+ "earn_meter_titan_multiplier" "Titan Boost-Meter Multiplikator"
+ "aegis_upgrades" "Aegis Upgrades"
+ "infinite_doomed_state" "Unendlicher Todgeweiht-Status"
+ "titan_shield_regen" "Regenerierende Schilde"
+
+ "riff_floorislava" "Tödlicher Boden"
+ "featured_mode_all_holopilot" "The Great Bamboozle"
+ "featured_mode_all_grapple" "Attack on Titanfall"
+ "featured_mode_all_phase" "The Otherside"
+ "featured_mode_all_ticks" "Scharf"
+ "featured_mode_tactikill" "Tactikill"
+ "featured_mode_amped_tacticals" "Verstärkte Tacticals"
+ "featured_mode_rocket_arena" "Rocket Arena"
+ "featured_mode_shotguns_snipers" "Bewaffnet und gefährlich"
+ "iron_rules" "Iron Titan Regeln"
+
+ "cp_amped_capture_points" "Verstärkte Hardpoints"
+ "coliseum_loadouts_enabled" "Coliseum Loadouts"
+
+ // northstar.custom localisation is just deciding not to work, so putting it here for now
+ "PL_sbox" "Sandbox"
+ "PL_sbox_lobby" "Sandbox Lobby"
+ "PL_sbox_desc" "Wie gmod, nur schlechter"
+ "PL_sbox_abbr" "SBOX"
+ "GAMEMODE_SBOX" "Sandbox"
+
+ "PL_gg" "Gun Game"
+ "PL_gg_lobby" "Gun Game Lobby"
+ "PL_gg_desc" "Erhalte einen Kill mit jeder Waffe um zu siegen."
+ "PL_gg_hint" "Erhalte einen Kill mit jeder Waffe um zu siegen."
+ "PL_gg_abbr" "GG"
+ "GAMEMODE_GG" "Gun Game"
+
+ "PL_tt" "Titan Tag"
+ "PL_tt_lobby" "Titan Tag Lobby"
+ "PL_tt_desc" "Erringe Punkte in deinem Titan. Zerstöre einen Titan für deinen Eigenen."
+ "PL_tt_hint" "Erringe Punkte in deinem Titan. Zerstöre einen Titan für deinen Eigenen."
+ "PL_tt_abbr" "TT"
+ "GAMEMODE_TT" "Titan Tag"
+
+ "PL_inf" "Infektion"
+ "PL_inf_lobby" "Infektion-Lobby"
+ "PL_inf_desc" "Überlebe die Infektion. Überlebende werden nach dem Tod infiziert."
+ "PL_inf_hint" "Überlebe die Infektion. Überlebende werden nach dem Tod infiziert."
+ "PL_inf_abbr" "INF"
+ "GAMEMODE_INF" "Infektion"
+ "INFECTION_YOU_ARE_INFECTED" "Du wurdest infiziert!"
+ "INFECTION_KILL_SURVIVORS" "Töte die restlichen Überlebenden."
+ "INFECTION_FIRST_INFECTED" "%s1 ist der erste Infizierte."
+ "INFECTION_LAST_SURVIVOR" "%s1 ist der letzte Überlebende!"
+ "INFECTION_KILL_LAST_SURVIVOR" "Infiziere sie bevor die Zeit abläuft!"
+ "INFECTION_YOU_ARE_LAST_SURVIVOR" "Du bist der letzte Überlebende!"
+ "INFECTION_SURVIVE_LAST_SURVIVOR" "Überlebe."
+
+ "PL_hs" "Hide and Seek"
+ "PL_hs_lobby" "Hide and Seek Lobby"
+ "PL_hs_desc" "Sucher versuchen, Versteckende zu finden."
+ "PL_hs_hint" "Sucher versuchen, Versteckende zu finden."
+ "PL_hs_abbr" "HS"
+ "GAMEMODE_hs" "Hide and Seek"
+ "HIDEANDSEEK_YOU_ARE_SEEKER" "DU BIST EIN SUCHER"
+ "HIDEANDSEEK_SEEKER_DESC" "Töte Versteckende mit Nahkampf.\nDu spawnst in %s1 Sekunden"
+ "HIDEANDSEEK_YOU_ARE_HIDER" "DU BIST VERSTECKENDER"
+ "HIDEANDSEEK_HIDER_DESC" "Verstecke dich vor Suchern."
+ "HIDEANDSEEK_SEEKERS_INCOMING" "SUCHER KOMMEN"
+ "HIDEANDSEEK_DONT_GET_FOUND" "Lass dich nicht finden!"
+ "HIDEANDSEEK_GET_LAST_HIDER" "%s1 ist der letzte Versteckende"
+ "HIDEANDSEEK_YOU_ARE_LAST_HIDER" "DU BIST DER LETZTE VERSTECKENDE"
+ "HIDEANDSEEK_GOT_STIM" "Du hast Stim! Lass dich nicht finden!"
+ "hideandseek_balance_teams" "Autobalance Hiders/Seekers"
+ "hideandseek_hiding_time" "Zeit zum Verstecken"
+
+ // these are defined in r1_english but titan war is a shit name so i'm changing it to another one that was referenced in development
+ "GAMEMODE_fw" "Grenzland-Krieg"
+ "PL_fw" "Grenzland-Krieg"
+ "PL_fw_lobby" "Grenzland-Krieg Lobby"
+ "PL_fw_desc" "Zerstöre den feindlichen Ernter und beschütze Euren"
+ "PL_fw_abbr" "GK"
+
+ "GAMEMODE_kr" "Verstärktes Killrace"
+ "PL_kr" "Verstärktes Killrace"
+ "PL_kr_lobby" "Verstärktes Killrace Lobby"
+ "PL_kr_desc" "Töte Piloten um die Länge deiner Killstreak zu erhöhen. Sammle die Fahne ein, um sie zu starten. Setze den Rekord, um zu gewinnen."
+ "PL_kr_hint" "Töte Piloten um die Länge deiner Killstreak zu erhöhen. Sammle die Fahne ein, um sie zu starten. Setze den Rekord, um zu gewinnen."
+ "PL_kr_abbr" "KR"
+ "SCOREBOARD_KR_RECORD" "Kill-Rekord"
+ "KR_NEW_RACER" "%s1 ist der Killracer"
+ "KR_YOU_ARE_NEW_RACER" "Du bist der Killracer"
+ "KR_YOU_SET_NEW_RECORD" "Setze einen neuen Kill-Rekord!"
+ "KR_FLAG_INCOMING" "Fahne erscheint"
+ "KR_COLLECT_FLAG" "Sammle sie ein um zum Killracer zu werden!"
+ "KR_ENEMY_KILLRACE_OVER" "%s1's Killstreak ist vorbei"
+ "KR_YOUR_KILLRACE_OVER" "Deine Killstreak ist vorbei"
+ "KR_YOUR_KILLRACE_SCORE" "Du hast %s1 Kills."
+
+ "GAMEMODE_fastball" "Fastball"
+ "PL_fastball" "Fastball"
+ "PL_fastball_lobby" "Fastball Lobby"
+ "PL_fastball_desc" "Permatod. Häcke Kontrollkonsolen um Punkte zu gewinnen und deine Kameraden zurückzuholen."
+ "PL_fastball_hint" "Permatod. Häcke Kontrollkonsolen um Punkte zu gewinnen und deine Kameraden zurückzuholen."
+ "PL_fastball_abbr" "FB"
+ "FASTBALL_PANEL_CAPTURED" "%s1 eroberte Konsole %s2"
+ "SCOREBOARD_FASTBALL_HACKS" "Konsolen erobert"
+
+ "GAMEMODE_ctf_comp" "Competitive CTF"
+
+ // mode settings
+ "MODE_SETTING_CATEGORY_PROMODE" "Pro-Modus"
+ "MODE_SETTING_CATEGORY_BLEEDOUT" "Pilot Bleedout"
+
+ "custom_air_accel_pilot" "Luftbeschleunigung"
+ "promode_enable" "Pro-mode Waffen"
+ "fp_embark_enabled" "Firstperson Einstiege/Exekutionen"
+ "classic_rodeo" "Klassisches Rodeo"
+ "oob_timer_enabled" "Out of Bounds Timer"
+ "riff_instagib" "Instagib Mode"
+
+ "riff_player_bleedout" "Piloten Bleedout"
+ "player_bleedout_forceHolster" "Waffen während Ausgeschaltet wegstecken"
+ "player_bleedout_forceDeathOnTeamBleedout" "Stirb bei Team Bleedout"
+ "player_bleedout_bleedoutTime" "Bleedout Zeit"
+ "player_bleedout_firstAidTime" "Erste Hilfe Zeit"
+ "player_bleedout_firstAidTimeSelf" "Self-res time"
+ "player_bleedout_firstAidHealPercent" "Erste Hilfe HP Prozent"
+ "player_bleedout_aiBleedingPlayerMissChance" "Ausgeschaltet AI Verfehl-Chance"
+
+ // coop stuff
+ "PL_sp_coop" "(UNFINISHED) Kampagne Koop"
+ "PL_sp_coop_lobby" "Kampagne Koop Lobby"
+ "PL_sp_coop_desc" "Spiele mit Freuden durch die Einzelspieler-Kampagne"
+ "PL_sp_coop_hint" "Spiele mit Freuden durch die Einzelspieler-Kampagne"
+ "PL_sp_coop_abbr" "SP"
+
+ "SP_TRAINING" "Der Piloten-Parkour"
+ "SP_TRAINING_CLASSIC_DESC" "Captain Lastimosas Trainingssimulation."
+
+ "SP_CRASHSITE" "BT-7274"
+ "SP_CRASHSITE_CLASSIC_DESC" "Jack Cooper trifft BT-7274."
+
+ "SP_SEWERS1" "Blut und Rost"
+ "SP_SEWERS1_CLASSIC_DESC" "Cooper und BT machen sich auf den Weg zum Treffpunkt mit Major Anderson."
+
+ "SP_BOOMTOWN_START" "In den Abgrund"
+ "SP_BOOMTOWN_START_CLASSIC_DESC" "Eine unterirdische Abkürzung zieht unerwartete Konsequenzen mit sich."
+
+ "SP_HUB_TIMESHIFT" "Wirkung und Ursache"
+ "SP_HUB_TIMESHIFT_CLASSIC_DESC" "An Major Andersons Koordinaten trifft ein seltsames Phänomen auf."
+
+ "SP_BEACON" "Der Sender"
+ "SP_BEACON_CLASSIC_DESC" "Cooper und BT versuchen, die Restflotte über die Pläne der IMC zu informieren."
+
+ "SP_TDAY" "Feuerprobe"
+ "SP_TDAY_CLASSIC_DESC" "Coopers Titan-Fähigkeiten werden im Kampf um die Lade auf eine harte Probe gestellt"
+
+ "SP_S2S" "Die Lade"
+ "SP_S2S_CLASSIC_DESC" "Cooper und BT gehen von Schiff zu Schiff, um die Lade zu finden."
+
+ "SP_SKYWAY_V1" "Die Faltwaffe"
+ "SP_SKYWAY_V1_CLASSIC_DESC" "BT und Cooper werden von Kuben Blist gefangen genommen."
+ }
+}
diff --git a/Northstar.Client/mod/resource/northstar_client_localisation_spanish.txt b/Northstar.Client/mod/resource/northstar_client_localisation_spanish.txt Binary files differnew file mode 100644 index 00000000..86f57a41 --- /dev/null +++ b/Northstar.Client/mod/resource/northstar_client_localisation_spanish.txt diff --git a/Northstar.Client/mod/resource/ui/menus/server_browser.menu b/Northstar.Client/mod/resource/ui/menus/server_browser.menu index 213f46e9..feca06fb 100644 --- a/Northstar.Client/mod/resource/ui/menus/server_browser.menu +++ b/Northstar.Client/mod/resource/ui/menus/server_browser.menu @@ -34,45 +34,1744 @@ resource/ui/menus/mods_browse.menu ControlName ImagePanel InheritProperties MenuTopBar } - + ButtonRowAnchor { ControlName Label labelText "" - xpos 120 - ypos 160 + xpos 130 + ypos 204 + } + + RowButtonsAnchor + { + ControlName Label + labelText "" + + xpos 94 + ypos 160 + } + + FilterButtonsRowAnchor + { + ControlName Label + labelText "" + + xpos 90 + ypos 807 + } + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + DarkenBackground + { + ControlName Label + classname ConnectingHUD + xpos 0 + ypos 0 + zpos 99 + wide %100 + tall %100 + labelText "" + bgcolor_override "0 0 0 160" + visible 1 + paintbackground 1 + } + + ConnectingAnimation + { + ControlName RuiPanel + classname ConnectingHUD + rui "ui/matchmaking_status_big.rpak" + + xpos 448 + ypos 284 + wide 1024 + tall 512 + + zpos 100 + visible 1 + } + + + + ConnectingLabel + { + ControlName Label + classname ConnectingHUD + wide 160 + tall 40 + ypos -310 + xpos -432 + labelText "#CONNECTING" + fgcolor_override "107 166 196 255" + textAlignment center + + visible 1 + zpos 101 + + pin_to_sibling ConnectingAnimation + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + + ConnectingLabelBackground + { + ControlName RuiPanel + classname ConnectingHUD + wide 160 + tall 40 + ypos -310 + xpos -432 + zpos 100 + + rui "ui/control_options_description.rpak" + + pin_to_sibling ConnectingAnimation + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + + ConnectingButton + { + ControlName RuiButton + classname ConnectingHUD + InheritProperties RuiSmallButton + labelText "#CANCEL" + textAlignment center + wide 100 + tall 40 + ypos -220 + xpos -462 + zpos 101 + + //fgcolor_override "107 166 196 255" + + pin_to_sibling ConnectingAnimation + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + ConnectingButtonBackground + { + ControlName RuiPanel + classname ConnectingHUD + wide 100 + tall 40 + ypos -220 + xpos -462 + zpos 100 + + rui "ui/control_options_description.rpak" + + pin_to_sibling ConnectingAnimation + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT } - - //test - //{ - // ControlName ListPanel - // xpos "200" - // ypos "200" - // zpos 999 - // wide "312" - // tall "340" - // autoResize "0" - // pinCorner "0" - // visible "1" - // enabled "1" - // tabPosition "0" - //} //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // PasswordProtected + + // BtnServerPasswordProtected (lock icons) dont pin to specified siblings + // Their 0,0 is the screen 0,0 + // Too lazy to fix hopefully doesn't break on other resolutions + BtnServerPasswordProtectedTab + { + ControlName ImagePanel + image "ui/menu/common/locked_icon" + scaleImage 1 + wide 48 + tall 48 + + pin_to_sibling RowButtonsAnchor + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner TOP_LEFT + } + + BtnServerPasswordProtected1 + { + ControlName ImagePanel + classname ServerLock + image "ui/menu/common/locked_icon" + scaleImage 1 + wide 48 + tall 48 + xpos 94 + ypos 200 + + pin_to_sibling BtnServer1 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerPasswordProtected2 + { + ControlName ImagePanel + classname ServerLock + image "ui/menu/common/locked_icon" + scaleImage 1 + wide 48 + tall 48 + xpos 94 + ypos 239 + + pin_to_sibling BtnServer2 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerPasswordProtected3 + { + ControlName ImagePanel + classname ServerLock + image "ui/menu/common/locked_icon" + scaleImage 1 + wide 48 + tall 48 + xpos 94 + ypos 279 + + pin_to_sibling BtnServer3 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerPasswordProtected4 + { + ControlName ImagePanel + classname ServerLock + image "ui/menu/common/locked_icon" + scaleImage 1 + wide 48 + tall 48 + xpos 94 + ypos 319 + + pin_to_sibling BtnServer4 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerPasswordProtected5 + { + ControlName ImagePanel + classname ServerLock + image "ui/menu/common/locked_icon" + scaleImage 1 + wide 48 + tall 48 + xpos 94 + ypos 359 + + pin_to_sibling BtnServer5 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerPasswordProtected6 + { + ControlName ImagePanel + classname ServerLock + image "ui/menu/common/locked_icon" + scaleImage 1 + wide 48 + tall 48 + xpos 94 + ypos 399 + + pin_to_sibling BtnServer6 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerPasswordProtected7 + { + ControlName ImagePanel + classname ServerLock + image "ui/menu/common/locked_icon" + scaleImage 1 + wide 48 + tall 48 + xpos 94 + ypos 439 + + pin_to_sibling BtnServer7 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerPasswordProtected8 + { + ControlName ImagePanel + classname ServerLock + image "ui/menu/common/locked_icon" + scaleImage 1 + wide 48 + tall 48 + xpos 94 + ypos 479 + + pin_to_sibling BtnServer8 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerPasswordProtected9 + { + ControlName ImagePanel + classname ServerLock + image "ui/menu/common/locked_icon" + scaleImage 1 + wide 48 + tall 48 + xpos 94 + ypos 519 + + pin_to_sibling BtnServer9 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerPasswordProtected10 + { + ControlName ImagePanel + classname ServerLock + image "ui/menu/common/locked_icon" + scaleImage 1 + wide 48 + tall 48 + xpos 94 + ypos 559 + + pin_to_sibling BtnServer10 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerPasswordProtected11 + { + ControlName ImagePanel + classname ServerLock + image "ui/menu/common/locked_icon" + scaleImage 1 + wide 48 + tall 48 + xpos 94 + ypos 399 + + pin_to_sibling BtnServer11 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerPasswordProtected12 + { + ControlName ImagePanel + classname ServerLock + image "ui/menu/common/locked_icon" + scaleImage 1 + wide 48 + tall 48 + xpos 94 + ypos 639 + + pin_to_sibling BtnServer12 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerPasswordProtected13 + { + ControlName ImagePanel + classname ServerLock + image "ui/menu/common/locked_icon" + scaleImage 1 + wide 48 + tall 48 + xpos 94 + ypos 679 + + pin_to_sibling BtnServer13 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerPasswordProtected14 + { + ControlName ImagePanel + classname ServerLock + image "ui/menu/common/locked_icon" + scaleImage 1 + wide 48 + tall 48 + xpos 94 + ypos 719 + + pin_to_sibling BtnServer14 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerPasswordProtected15 + { + ControlName ImagePanel + classname ServerLock + image "ui/menu/common/locked_icon" + scaleImage 1 + wide 48 + tall 48 + xpos 94 + ypos 759 + + pin_to_sibling BtnServer15 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + + // Name + BtnServerNameTab + { + ControlName RuiButton + InheritProperties RuiSmallButton + labelText "#SERVERS_COLUMN" + wide 600 + xpos -4 + ypos -1 + + scriptID 999 + + pin_to_sibling BtnServerPasswordProtectedTab + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner TOP_RIGHT + navDown BtnServer1 + navRight BtnServerPlayersTab + } + + BtnServerName0 + { + ControlName Label + labelText "" + textAlignment west + classname ServerName + wide 586 + tall 44 + ypos -44 + xpos -14 + interactive false + + + pin_to_sibling BtnServer1 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerName1 + { + ControlName Label + labelText "" + textAlignment west + classname ServerName + wide 586 + tall 44 + ypos -44 + xpos -14 + + pin_to_sibling BtnServer2 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerName2 + { + ControlName Label + labelText "" + textAlignment west + classname ServerName + wide 586 + tall 44 + ypos -44 + xpos -14 + + pin_to_sibling BtnServer3 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerName3 + { + ControlName Label + labelText "" + textAlignment west + classname ServerName + wide 586 + tall 44 + ypos -44 + xpos -14 + + pin_to_sibling BtnServer4 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerName4 + { + ControlName Label + labelText "" + textAlignment west + classname ServerName + wide 586 + tall 44 + ypos -44 + xpos -14 + + pin_to_sibling BtnServer5 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerName5 + { + ControlName Label + labelText "" + textAlignment west + classname ServerName + wide 586 + tall 44 + ypos -44 + xpos -14 + + pin_to_sibling BtnServer6 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerName6 + { + ControlName Label + labelText "" + textAlignment west + classname ServerName + wide 586 + tall 44 + ypos -44 + xpos -14 + + pin_to_sibling BtnServer7 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerName7 + { + ControlName Label + labelText "" + textAlignment west + classname ServerName + wide 586 + tall 44 + ypos -44 + xpos -14 + + pin_to_sibling BtnServer8 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerName8 + { + ControlName Label + labelText "" + textAlignment west + classname ServerName + wide 586 + tall 44 + ypos -44 + xpos -14 + + pin_to_sibling BtnServer9 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerName9 + { + ControlName Label + labelText "" + textAlignment west + classname ServerName + wide 586 + tall 44 + ypos -44 + xpos -14 + + pin_to_sibling BtnServer10 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerName10 + { + ControlName Label + labelText "" + textAlignment west + classname ServerName + wide 586 + tall 44 + ypos -44 + xpos -14 + + pin_to_sibling BtnServer11 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerName11 + { + ControlName Label + labelText "" + textAlignment west + classname ServerName + wide 586 + tall 44 + ypos -44 + xpos -14 + + pin_to_sibling BtnServer12 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerName12 + { + ControlName Label + labelText "" + textAlignment west + classname ServerName + wide 586 + tall 44 + ypos -44 + xpos -14 + + pin_to_sibling BtnServer13 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerName13 + { + ControlName Label + labelText "" + textAlignment west + classname ServerName + wide 586 + tall 44 + ypos -44 + xpos -14 + + pin_to_sibling BtnServer14 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerName14 + { + ControlName Label + labelText "" + textAlignment west + classname ServerName + wide 586 + tall 44 + ypos -44 + xpos -14 + + pin_to_sibling BtnServer15 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + + + // Players + BtnServerPlayersTab + { + ControlName RuiButton + InheritProperties RuiSmallButton + labelText "#PLAYERS_COLUMN" + wide 104 + xpos 4 + + scriptID 999 + + pin_to_sibling BtnServerNameTab + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner TOP_RIGHT + navDown BtnServer1 + navLeft BtnServerNameTab + navRight BtnServerMapTab + } + + BtnServerPlayers1 + { + ControlName Label + labelText "" + classname PlayerCount + textAlignment center + wide 104 + tall 44 + ypos -44 + xpos -600 + + pin_to_sibling BtnServer1 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerPlayers2 + { + ControlName Label + labelText "" + classname PlayerCount + textAlignment center + wide 104 + tall 44 + ypos -44 + xpos -600 + + pin_to_sibling BtnServer2 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerPlayers3 + { + ControlName Label + labelText "" + classname PlayerCount + textAlignment center + wide 104 + tall 44 + ypos -44 + xpos -600 + + pin_to_sibling BtnServer3 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerPlayers4 + { + ControlName Label + labelText "" + classname PlayerCount + textAlignment center + wide 104 + tall 44 + ypos -44 + xpos -600 + + pin_to_sibling BtnServer4 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerPlayers5 + { + ControlName Label + labelText "" + classname PlayerCount + textAlignment center + wide 104 + tall 44 + ypos -44 + xpos -600 + + pin_to_sibling BtnServer5 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerPlayers6 + { + ControlName Label + labelText "" + classname PlayerCount + textAlignment center + wide 104 + tall 44 + ypos -44 + xpos -600 + + pin_to_sibling BtnServer6 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerPlayers7 + { + ControlName Label + labelText "" + classname PlayerCount + textAlignment center + wide 104 + tall 44 + ypos -44 + xpos -600 + + pin_to_sibling BtnServer7 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerPlayers8 + { + ControlName Label + labelText "" + classname PlayerCount + textAlignment center + wide 104 + tall 44 + ypos -44 + xpos -600 + + pin_to_sibling BtnServer8 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerPlayers9 + { + ControlName Label + labelText "" + classname PlayerCount + textAlignment center + wide 104 + tall 44 + ypos -44 + xpos -600 + + pin_to_sibling BtnServer9 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerPlayers10 + { + ControlName Label + labelText "" + classname PlayerCount + textAlignment center + wide 104 + tall 44 + ypos -44 + xpos -600 + + pin_to_sibling BtnServer10 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerPlayers11 + { + ControlName Label + labelText "" + classname PlayerCount + textAlignment center + wide 104 + tall 44 + ypos -44 + xpos -600 + + pin_to_sibling BtnServer11 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerPlayers12 + { + ControlName Label + labelText "" + classname PlayerCount + textAlignment center + wide 104 + tall 44 + ypos -44 + xpos -600 + + pin_to_sibling BtnServer12 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerPlayers13 + { + ControlName Label + labelText "16/16" + classname PlayerCount + textAlignment center + wide 104 + tall 44 + ypos -44 + xpos -600 + + pin_to_sibling BtnServer13 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerPlayers14 + { + ControlName Label + labelText "8/8" + classname PlayerCount + textAlignment center + wide 104 + tall 44 + ypos -44 + xpos -600 + + pin_to_sibling BtnServer14 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerPlayers15 + { + ControlName Label + labelText "" + classname PlayerCount + textAlignment center + wide 104 + tall 44 + ypos -44 + xpos -600 + + pin_to_sibling BtnServer15 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + + // Map + BtnServerMapTab + { + ControlName RuiButton + InheritProperties RuiSmallButton + labelText "#MAP_COLUMN" + textAlignment center + wide 140 + xpos 4 + + scriptID 999 + + pin_to_sibling BtnServerPlayersTab + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner TOP_RIGHT + navDown BtnServer1 + navLeft BtnServerPlayersTab + navRight BtnServerGamemodeTab + } + + BtnServerMap1 + { + ControlName Label + labelText "" + classname ServerMap + wide 140 + textAlignment center + tall 44 + ypos -44 + xpos -709 + + pin_to_sibling BtnServer1 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerMap2 + { + ControlName Label + labelText "" + classname ServerMap + wide 140 + textAlignment center + tall 44 + ypos -44 + xpos -709 + + pin_to_sibling BtnServer2 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerMap3 + { + ControlName Label + labelText "" + classname ServerMap + wide 140 + textAlignment center + tall 44 + ypos -44 + xpos -709 + + pin_to_sibling BtnServer3 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerMap4 + { + ControlName Label + labelText "" + classname ServerMap + wide 140 + textAlignment center + tall 44 + ypos -44 + xpos -709 + + pin_to_sibling BtnServer4 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerMap5 + { + ControlName Label + labelText "" + classname ServerMap + wide 140 + textAlignment center + tall 44 + ypos -44 + xpos -709 + + pin_to_sibling BtnServer5 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerMap6 + { + ControlName Label + labelText "" + classname ServerMap + wide 140 + textAlignment center + tall 44 + ypos -44 + xpos -709 + + pin_to_sibling BtnServer6 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerMap7 + { + ControlName Label + labelText "" + classname ServerMap + wide 140 + textAlignment center + tall 44 + ypos -44 + xpos -709 + + pin_to_sibling BtnServer7 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerMap8 + { + ControlName Label + labelText "" + classname ServerMap + wide 140 + textAlignment center + tall 44 + ypos -44 + xpos -709 + + pin_to_sibling BtnServer8 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerMap9 + { + ControlName Label + labelText "" + classname ServerMap + wide 140 + textAlignment center + tall 44 + ypos -44 + xpos -709 + + pin_to_sibling BtnServer9 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerMap10 + { + ControlName Label + labelText "" + classname ServerMap + wide 140 + textAlignment center + tall 44 + ypos -44 + xpos -709 + + pin_to_sibling BtnServer10 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerMap11 + { + ControlName Label + labelText "" + classname ServerMap + wide 140 + textAlignment center + tall 44 + ypos -44 + xpos -709 + + pin_to_sibling BtnServer11 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerMap12 + { + ControlName Label + labelText "" + classname ServerMap + wide 140 + textAlignment center + tall 44 + ypos -44 + xpos -709 + + pin_to_sibling BtnServer12 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerMap13 + { + ControlName Label + labelText "" + classname ServerMap + wide 140 + textAlignment center + tall 44 + ypos -44 + xpos -709 + + pin_to_sibling BtnServer13 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerMap14 + { + ControlName Label + labelText "" + classname ServerMap + wide 140 + textAlignment center + tall 44 + ypos -44 + xpos -709 + + pin_to_sibling BtnServer14 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerMap15 + { + ControlName Label + labelText "" + classname ServerMap + wide 140 + textAlignment center + tall 44 + ypos -44 + xpos -709 + + pin_to_sibling BtnServer15 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + + // Gamemode + BtnServerGamemodeTab + { + ControlName RuiButton + InheritProperties RuiSmallButton + labelText "#GAMEMODE_COLUMN" + wide 150 + xpos 4 + + scriptID 999 + + pin_to_sibling BtnServerMapTab + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner TOP_RIGHT + navDown BtnServer1 + navLeft BtnServerMapTab + navRight BtnServerLatencyTab + } + + BtnServerGamemode1 + { + ControlName Label + labelText "" + classname ServerGamemode + wide 140 + textAlignment center + tall 44 + ypos -44 + xpos -860 + + pin_to_sibling BtnServer1 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerGamemode2 + { + ControlName Label + labelText "" + classname ServerGamemode + wide 140 + textAlignment center + tall 44 + ypos -44 + xpos -860 + + pin_to_sibling BtnServer2 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerGamemode3 + { + ControlName Label + labelText "" + classname ServerGamemode + wide 140 + textAlignment center + tall 44 + ypos -44 + xpos -860 + + pin_to_sibling BtnServer3 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerGamemode4 + { + ControlName Label + labelText "" + classname ServerGamemode + wide 140 + textAlignment center + tall 44 + ypos -44 + xpos -860 + + pin_to_sibling BtnServer4 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerGamemode5 + { + ControlName Label + labelText "" + classname ServerGamemode + wide 140 + textAlignment center + tall 44 + ypos -44 + xpos -860 + + pin_to_sibling BtnServer5 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerGamemode6 + { + ControlName Label + labelText "" + classname ServerGamemode + wide 140 + textAlignment center + tall 44 + ypos -44 + xpos -860 + + pin_to_sibling BtnServer6 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerGamemode7 + { + ControlName Label + labelText "" + classname ServerGamemode + wide 140 + textAlignment center + tall 44 + ypos -44 + xpos -860 + + pin_to_sibling BtnServer7 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerGamemode8 + { + ControlName Label + labelText "" + classname ServerGamemode + wide 140 + textAlignment center + tall 44 + ypos -44 + xpos -860 + + pin_to_sibling BtnServer8 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerGamemode9 + { + ControlName Label + labelText "" + classname ServerGamemode + wide 140 + textAlignment center + tall 44 + ypos -44 + xpos -860 + + pin_to_sibling BtnServer9 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerGamemode10 + { + ControlName Label + labelText "" + classname ServerGamemode + wide 140 + textAlignment center + tall 44 + ypos -44 + xpos -860 + + pin_to_sibling BtnServer10 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerGamemode11 + { + ControlName Label + labelText "" + classname ServerGamemode + wide 140 + textAlignment center + tall 44 + ypos -44 + xpos -860 + + pin_to_sibling BtnServer11 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerGamemode12 + { + ControlName Label + labelText "" + classname ServerGamemode + wide 140 + textAlignment center + tall 44 + ypos -44 + xpos -860 + + pin_to_sibling BtnServer12 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerGamemode13 + { + ControlName Label + labelText "" + classname ServerGamemode + wide 140 + textAlignment center + tall 44 + ypos -44 + xpos -860 + + pin_to_sibling BtnServer13 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerGamemode14 + { + ControlName Label + labelText "" + classname ServerGamemode + wide 140 + textAlignment center + tall 44 + ypos -44 + xpos -860 + + pin_to_sibling BtnServer14 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerGamemode15 + { + ControlName Label + labelText "" + classname ServerGamemode + wide 140 + textAlignment center + tall 44 + ypos -44 + xpos -860 + + pin_to_sibling BtnServer15 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + + // Latency + BtnServerLatencyTab + { + ControlName RuiButton + InheritProperties RuiSmallButton + labelText "#LATENCY_COLUMN" + wide 110 + xpos 4 + + scriptID 999 + + pin_to_sibling BtnServerGamemodeTab + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner TOP_RIGHT + navDown BtnServer1 + navLeft BtnServerGamemodeTab + navLeft BtnServerJoin + } + + BtnServerLatency1 + { + ControlName Label + labelText "" + classname Serverlatency + textAlignment center + wide 110 + tall 44 + ypos -44 + xpos -1006 + + pin_to_sibling BtnServer1 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerLatency2 + { + ControlName Label + labelText "" + classname Serverlatency + textAlignment center + wide 110 + tall 44 + ypos -44 + xpos -1006 + + pin_to_sibling BtnServer2 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerLatency3 + { + ControlName Label + labelText "" + classname Serverlatency + textAlignment center + wide 110 + tall 44 + ypos -44 + xpos -1006 + + pin_to_sibling BtnServer3 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerLatency4 + { + ControlName Label + labelText "" + classname Serverlatency + textAlignment center + wide 110 + tall 44 + ypos -44 + xpos -1006 + + pin_to_sibling BtnServer4 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerLatency5 + { + ControlName Label + labelText "" + classname Serverlatency + textAlignment center + wide 110 + tall 44 + ypos -44 + xpos -1006 + + pin_to_sibling BtnServer5 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerLatency6 + { + ControlName Label + labelText "" + classname Serverlatency + textAlignment center + wide 110 + tall 44 + ypos -44 + xpos -1006 + + pin_to_sibling BtnServer6 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerLatency7 + { + ControlName Label + labelText "" + classname Serverlatency + textAlignment center + wide 110 + tall 44 + ypos -44 + xpos -1006 + + pin_to_sibling BtnServer7 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerLatency8 + { + ControlName Label + labelText "" + classname Serverlatency + textAlignment center + wide 110 + tall 44 + ypos -44 + xpos -1006 + + pin_to_sibling BtnServer8 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerLatency9 + { + ControlName Label + labelText "" + classname Serverlatency + textAlignment center + wide 110 + tall 44 + ypos -44 + xpos -1006 + + pin_to_sibling BtnServer9 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerLatency10 + { + ControlName Label + labelText "" + classname Serverlatency + textAlignment center + wide 110 + tall 44 + ypos -44 + xpos -1006 + + pin_to_sibling BtnServer10 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerLatency11 + { + ControlName Label + labelText "" + classname Serverlatency + textAlignment center + wide 110 + tall 44 + ypos -44 + xpos -1006 + + pin_to_sibling BtnServer11 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerLatency12 + { + ControlName Label + labelText "" + classname Serverlatency + textAlignment center + wide 110 + tall 44 + ypos -44 + xpos -1006 + + pin_to_sibling BtnServer12 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerLatency13 + { + ControlName Label + labelText "" + classname Serverlatency + textAlignment center + wide 110 + tall 44 + ypos -44 + xpos -1006 + + pin_to_sibling BtnServer13 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerLatency14 + { + ControlName Label + labelText "" + classname Serverlatency + textAlignment center + wide 110 + tall 44 + ypos -44 + xpos -1006 + + pin_to_sibling BtnServer14 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerLatency15 + { + ControlName Label + labelText "" + classname Serverlatency + textAlignment center + wide 110 + tall 44 + ypos -44 + xpos -1006 + + pin_to_sibling BtnServer15 + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + + // Dividers: + + // Y + YDivider0 + { + ControlName ImagePanel + wide 2 + tall 641 + visible 1 + image "vgui/hud/white" + drawColor "160 157 149 255" + scaleImage 1 + xpos 3 + ypos -1 + + pin_to_sibling BtnServerNameTab + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner TOP_LEFT + } + + YDivider1 + { + ControlName ImagePanel + wide 2 + tall 641 + visible 1 + image "vgui/hud/white" + drawColor "160 157 149 255" + scaleImage 1 + xpos 3 + ypos -1 + + pin_to_sibling BtnServerPlayersTab + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner TOP_LEFT + } + + YDivider2 + { + ControlName ImagePanel + wide 2 + tall 641 + visible 1 + image "vgui/hud/white" + drawColor "160 157 149 255" + scaleImage 1 + xpos 3 + ypos -1 + + pin_to_sibling BtnServerMapTab + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner TOP_LEFT + } + + YDivider3 + { + ControlName ImagePanel + wide 2 + tall 641 + visible 1 + image "vgui/hud/white" + drawColor "160 157 149 255" + scaleImage 1 + xpos 3 + ypos -1 + + pin_to_sibling BtnServerGamemodeTab + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner TOP_LEFT + } + + YDivider4 + { + ControlName ImagePanel + wide 2 + tall 641 + visible 1 + image "vgui/hud/white" + drawColor "160 157 149 255" + scaleImage 1 + xpos 3 + ypos -1 + + pin_to_sibling BtnServerLatencyTab + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner TOP_LEFT + } + + // X + XDivider0 + { + ControlName ImagePanel + wide 1150 + tall 2 + visible 1 + image "vgui/hud/white" + drawColor "160 157 149 255" + scaleImage 1 + ypos 3 + xpos 37 + + pin_to_sibling BtnServerNameTab + pin_corner_to_sibling BOTTOM_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + + // List: + BtnServerDummmyTop { + ControlName RuiButton + visible 1 + width 0 + height 0 + } BtnServer1 { ControlName RuiButton InheritProperties RuiSmallButton classname ServerButton scriptID 0 - navUp BtnServer15 - navDown BtnServer2 + wide 1120 + xpos -8 - pin_to_sibling ButtonRowAnchor - pin_corner_to_sibling TOP_LEFT - pin_to_sibling_corner TOP_LEFT + pin_to_sibling ButtonRowAnchor + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner TOP_LEFT + navUp BtnServerDummmyTop + navRight BtnServerJoin + navDown BtnServer2 } BtnServer2 { @@ -80,11 +1779,14 @@ resource/ui/menus/mods_browse.menu InheritProperties RuiSmallButton classname ServerButton scriptID 1 + wide 1120 + pin_to_sibling BtnServer1 pin_corner_to_sibling TOP_LEFT pin_to_sibling_corner BOTTOM_LEFT navUp BtnServer1 navDown BtnServer3 + navRight BtnServerJoin } BtnServer3 { @@ -92,11 +1794,14 @@ resource/ui/menus/mods_browse.menu InheritProperties RuiSmallButton classname ServerButton scriptID 2 + wide 1120 + pin_to_sibling BtnServer2 pin_corner_to_sibling TOP_LEFT pin_to_sibling_corner BOTTOM_LEFT navUp BtnServer2 navDown BtnServer4 + navRight BtnServerJoin } BtnServer4 { @@ -104,12 +1809,15 @@ resource/ui/menus/mods_browse.menu InheritProperties RuiSmallButton classname ServerButton scriptID 3 + wide 1120 + pin_to_sibling BtnServer3 pin_corner_to_sibling TOP_LEFT pin_to_sibling_corner BOTTOM_LEFT //ypos 11 navUp BtnServer3 navDown BtnServer5 + navRight BtnServerJoin } BtnServer5 { @@ -117,11 +1825,14 @@ resource/ui/menus/mods_browse.menu InheritProperties RuiSmallButton classname ServerButton scriptID 4 + wide 1120 + pin_to_sibling BtnServer4 pin_corner_to_sibling TOP_LEFT pin_to_sibling_corner BOTTOM_LEFT navUp BtnServer4 navDown BtnServer6 + navRight BtnServerJoin } BtnServer6 { @@ -129,11 +1840,14 @@ resource/ui/menus/mods_browse.menu InheritProperties RuiSmallButton classname ServerButton scriptID 5 + wide 1120 + pin_to_sibling BtnServer5 pin_corner_to_sibling TOP_LEFT pin_to_sibling_corner BOTTOM_LEFT navUp BtnServer5 navDown BtnServer7 + navRight BtnServerJoin } BtnServer7 { @@ -141,11 +1855,14 @@ resource/ui/menus/mods_browse.menu InheritProperties RuiSmallButton classname ServerButton scriptID 6 + wide 1120 + pin_to_sibling BtnServer6 pin_corner_to_sibling TOP_LEFT pin_to_sibling_corner BOTTOM_LEFT navUp BtnServer6 navDown BtnServer8 + navRight BtnServerJoin } BtnServer8 { @@ -153,11 +1870,14 @@ resource/ui/menus/mods_browse.menu InheritProperties RuiSmallButton classname ServerButton scriptID 7 + wide 1120 + pin_to_sibling BtnServer7 pin_corner_to_sibling TOP_LEFT pin_to_sibling_corner BOTTOM_LEFT navUp BtnServer7 navDown BtnServer9 + navRight BtnServerJoin } BtnServer9 { @@ -165,11 +1885,14 @@ resource/ui/menus/mods_browse.menu InheritProperties RuiSmallButton classname ServerButton scriptID 8 + wide 1120 + pin_to_sibling BtnServer8 pin_corner_to_sibling TOP_LEFT pin_to_sibling_corner BOTTOM_LEFT navUp BtnServer8 navDown BtnServer10 + navRight BtnServerJoin } BtnServer10 { @@ -177,11 +1900,14 @@ resource/ui/menus/mods_browse.menu InheritProperties RuiSmallButton classname ServerButton scriptID 9 + wide 1120 + pin_to_sibling BtnServer9 pin_corner_to_sibling TOP_LEFT pin_to_sibling_corner BOTTOM_LEFT navUp BtnServer9 navDown BtnServer11 + navRight BtnServerJoin } BtnServer11 { @@ -189,11 +1915,14 @@ resource/ui/menus/mods_browse.menu InheritProperties RuiSmallButton classname ServerButton scriptID 10 + wide 1120 + pin_to_sibling BtnServer10 pin_corner_to_sibling TOP_LEFT pin_to_sibling_corner BOTTOM_LEFT navUp BtnServer10 navDown BtnServer12 + navRight BtnServerJoin } BtnServer12 { @@ -201,11 +1930,14 @@ resource/ui/menus/mods_browse.menu InheritProperties RuiSmallButton classname ServerButton scriptID 11 + wide 1120 + pin_to_sibling BtnServer11 pin_corner_to_sibling TOP_LEFT pin_to_sibling_corner BOTTOM_LEFT navUp BtnServer11 navDown BtnServer13 + navRight BtnServerJoin } BtnServer13 { @@ -213,11 +1945,14 @@ resource/ui/menus/mods_browse.menu InheritProperties RuiSmallButton classname ServerButton scriptID 12 + wide 1120 + pin_to_sibling BtnServer12 pin_corner_to_sibling TOP_LEFT pin_to_sibling_corner BOTTOM_LEFT navUp BtnServer12 navDown BtnServer14 + navRight BtnServerJoin } BtnServer14 { @@ -225,11 +1960,14 @@ resource/ui/menus/mods_browse.menu InheritProperties RuiSmallButton classname ServerButton scriptID 13 + wide 1120 + pin_to_sibling BtnServer13 pin_corner_to_sibling TOP_LEFT pin_to_sibling_corner BOTTOM_LEFT navUp BtnServer13 navDown BtnServer15 + navRight BtnServerJoin } BtnServer15 { @@ -237,28 +1975,419 @@ resource/ui/menus/mods_browse.menu InheritProperties RuiSmallButton classname ServerButton scriptID 14 + wide 1120 + pin_to_sibling BtnServer14 pin_corner_to_sibling TOP_LEFT pin_to_sibling_corner BOTTOM_LEFT navUp BtnServer14 - navDown BtnServer1 + navRight BtnServerJoin + navDown BtnServerDummmyBottom + } + BtnServerDummmyBottom { + ControlName RuiButton + visible 1 + width 0 + height 0 } - + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + BtnServerListUpArrow + { + ControlName RuiButton + InheritProperties RuiSmallButton + //labelText "A" + wide 40 + tall 40 + xpos 2 + ypos 2 + + image "vgui/hud/white" + drawColor "255 255 255 128" + + pin_to_sibling ServerDetailsPanel + pin_corner_to_sibling TOP_RIGHT + pin_to_sibling_corner TOP_LEFT + } + + BtnServerListUpArrowPanel + { + ControlName RuiPanel + wide 40 + tall 40 + xpos 2 + ypos 2 + + rui "ui/control_options_description.rpak" + + visible 1 + zpos -1 + + pin_to_sibling ServerDetailsPanel + pin_corner_to_sibling TOP_RIGHT + pin_to_sibling_corner TOP_LEFT + } + + BtnServerListDownArrow + { + ControlName RuiButton + InheritProperties RuiSmallButton + //labelText "V" + wide 40 + tall 40 + xpos 2 + ypos -604 + + image "vgui/hud/white" + drawColor "255 255 255 128" + + pin_to_sibling ServerDetailsPanel + pin_corner_to_sibling TOP_RIGHT + pin_to_sibling_corner TOP_LEFT + } + + BtnServerListDownArrowPanel + { + ControlName RuiPanel + wide 40 + tall 40 + xpos 2 + ypos -604 + + rui "ui/control_options_description.rpak" + + visible 1 + zpos -1 + + pin_to_sibling ServerDetailsPanel + pin_corner_to_sibling TOP_RIGHT + pin_to_sibling_corner TOP_LEFT + } + + BtnServerListSlider + { + ControlName RuiButton + InheritProperties RuiSmallButton + //labelText "V" + wide 40 + tall 562 + xpos 2 + ypos -40 + zpos 0 + + image "vgui/hud/white" + drawColor "255 255 255 128" + + pin_to_sibling ServerDetailsPanel + pin_corner_to_sibling TOP_RIGHT + pin_to_sibling_corner TOP_LEFT + } + + BtnServerListSliderPanel + { + ControlName RuiPanel + wide 40 + tall 562 + xpos 2 + ypos -40 + + rui "ui/control_options_description.rpak" + + visible 1 + zpos -1 + + pin_to_sibling ServerDetailsPanel + pin_corner_to_sibling TOP_RIGHT + pin_to_sibling_corner TOP_LEFT + } + + // sh_menu_models.gnut has a global function which gets called when + // left mouse button gets called while hovering and has mouse + // deltaX; deltaY which we can yoink for ourselfes + MouseMovementCapture + { + ControlName CMouseMovementCapturePanel + wide 40 + tall 562 + xpos 2 + ypos -40 + zpos 1 + + pin_to_sibling ServerDetailsPanel + pin_corner_to_sibling TOP_RIGHT + pin_to_sibling_corner TOP_LEFT + } + + //MouseMovementCaptureaaaaaaa + //{ + // ControlName CMouseMovementCapturePanel + // wide %100 + // tall %100 + // xpos 0 + // ypos 0 + // zpos 2 + //} + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - LabelDetails + + FilterPanel + { + ControlName RuiPanel + wide 1200 + tall 153 + xpos -8 + + rui "ui/control_options_description.rpak" + + visible 1 + zpos -1 + + pin_to_sibling FilterButtonsRowAnchor + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner TOP_LEFT + } + + BtnSearchLabel + { + ControlName RuiButton + InheritProperties RuiSmallButton + labelText "#SEARCHBAR_LABEL" + textAlignment west + wide 500 + xpos -23 + ypos -16 + + wrap 1 + visible 1 + zpos 0 + + pin_to_sibling FilterButtonsRowAnchor + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner TOP_LEFT + + navUp BtnServer15 + navDown SwtBtnSelectMap + } + + BtnServerSearch + { + ControlName TextEntry + zpos 100 // This works around input weirdness when the control is constructed by code instead of VGUI blackbox. + xpos -400 + ypos -5 + wide 390 + tall 30 + textHidden 0 + editable 1 + font Default_21 + allowRightClickMenu 0 + allowSpecialCharacters 0 + unicode 0 + + pin_to_sibling BtnSearchLabel + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner TOP_RIGHT + + navUp BtnServer15 + navDown SwtBtnSelectMap + } + + SwtBtnSelectMap + { + ControlName RuiButton + InheritProperties SwitchButton + labelText "#MAP_FILTER" + ConVar "filter_map" + wide 500 + + pin_to_sibling BtnSearchLabel + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + + navUp BtnServerSearch + navDown SwtBtnSelectGamemode + } + + SwtBtnSelectGamemode + { + ControlName RuiButton + InheritProperties SwitchButton + labelText "#GAMEMODE_FILTER" + ConVar "filter_gamemode" + wide 500 + + pin_to_sibling SwtBtnSelectMap + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + + navUp SwtBtnSelectMap + navDown SwtBtnHideFull + } + + SwtBtnHideFull + { + ControlName RuiButton + InheritProperties SwitchButton + labelText "#HIDE_FULL_FILTER" + ConVar "filter_hide_full" + wide 500 + + + list + { + "#SWITCH_NO" 0 + "#SWITCH_YES" 1 + } + + pin_to_sibling BtnSearchLabel + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner TOP_RIGHT + + navUp SwtBtnSelectGamemode + navDown SwtBtnHideEmpty + } + + SwtBtnHideEmpty + { + ControlName RuiButton + InheritProperties SwitchButton + labelText "#HIDE_EMPTY_FILTER" + ConVar "filter_hide_empty" + wide 500 + + + list + { + "#SWITCH_NO" 0 + "#SWITCH_YES" 1 + } + + pin_to_sibling SwtBtnHideFull + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + + navUp SwtBtnHideFull + navDown SwtBtnHideProtected + } + + SwtBtnHideProtected + { + ControlName RuiButton + InheritProperties SwitchButton + labelText "#HIDE_PROT_FILTER" + ConVar "filter_hide_protected" + wide 500 + + list + { + "#SWITCH_NO" 0 + "#SWITCH_YES" 1 + } + + pin_to_sibling SwtBtnHideEmpty + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + + navUp SwtBtnHideEmpty + navDown BtnFiltersClear + } + + BtnFiltersClear + { + ControlName RuiButton + InheritProperties RuiSmallButton + labelText "#CLEAR_FILTERS" + wide 100 + xpos -17 + ypos -57 + zpos 90 + + scriptID 999 + + pin_to_sibling FilterPanel + pin_corner_to_sibling TOP_RIGHT + pin_to_sibling_corner BOTTOM_RIGHT + + navUp SwtBtnHideProtected + navDown BtnDummyAfterFilterClear + navLeft SwtBtnHideProtected + navRight BtnServerJoin + } + + BtnDummyAfterFilterClear { + ControlName RuiButton + width 0 + height 0 + visible 1 + } +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + ServerDetailsPanel { ControlName RuiPanel - xpos 675 + xpos 1300 ypos 160 tall 800 - wide 950 - rui "ui/knowledgebase_panel.rpak" + wide 500 + rui "ui/control_options_description.rpak" wrap 1 visible 1 zpos -1 } + //TestPanel + //{ + // ControlName RuiPanel + // tall 420 + // wide 500 + // ypos -330 + // rui "ui/control_options_description.rpak" + // wrap 1 + // visible 1 + // zpos -1 + + // pin_to_sibling ServerDetailsPanel + // pin_corner_to_sibling TOP_LEFT + // pin_to_sibling_corner TOP_LEFT + //} + + LabelDescription + { + ControlName Label + textAlignment north + tall 420 + wide 460 + ypos -300 + xpos -20 + + wrap 1 + visible 1 + zpos -1 + + pin_to_sibling ServerDetailsPanel + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner TOP_LEFT + } + + LabelMods + { + ControlName Label + textAlignment north + tall 420 + wide 460 + ypos -330 + xpos -20 + + wrap 1 + visible 1 + zpos -1 + + pin_to_sibling ServerDetailsPanel + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner TOP_LEFT + } + NextMapImage { ControlName RuiPanel @@ -269,17 +2398,32 @@ resource/ui/menus/mods_browse.menu rui "ui/basic_menu_image.rpak" - pin_to_sibling LabelDetails + pin_to_sibling ServerDetailsPanel pin_corner_to_sibling TOP_RIGHT pin_to_sibling_corner TOP_RIGHT } + + NextMapBack + { + ControlName RuiPanel + tall 288 + wide 500 + rui "ui/control_options_description.rpak" + visible 0 + zpos 1 + + pin_to_sibling ServerDetailsPanel + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner TOP_LEFT + } + NextMapName { ControlName Label pin_to_sibling NextMapImage pin_corner_to_sibling BOTTOM_RIGHT pin_to_sibling_corner BOTTOM_RIGHT - + xpos -12 ypos 0 zpos 1 @@ -327,8 +2471,121 @@ resource/ui/menus/mods_browse.menu pin_to_sibling_corner TOP_RIGHT } + ServerName + { + ControlName Label + ypos -8 + xpos -8 + zpos 1 + wide 492 + tall 88 + wrap 1 + textAlignment north + + //labelText "Server name" + use_proportional_insets 1 + textinsetx 2 + font Default_28_DropShadow + allcaps 1 + fgcolor_override "255 255 255 255" + + pin_to_sibling ServerDetailsPanel + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner TOP_LEFT + } + + BtnServerDescription + { + ControlName RuiButton + InheritProperties RuiSmallButton + labelText "#DESCRIPTION" + + textAlignment center + wide 250 + zpos 999 + ypos -1 + + pin_to_sibling NextMapImage + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + BtnServerMods + { + ControlName RuiButton + InheritProperties RuiSmallButton + labelText "#MODS" + + textAlignment center + wide 250 + zpos 999 + + pin_to_sibling BtnServerDescription + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner TOP_RIGHT + } + + BtnServerJoin + { + ControlName RuiButton + InheritProperties RuiSmallButton + labelText "#JOIN_BUTTON" + wide 80 + xpos -17 + ypos -57 + zpos 90 + + scriptID 999 + + pin_to_sibling ServerDetailsPanel + pin_corner_to_sibling TOP_RIGHT + pin_to_sibling_corner BOTTOM_RIGHT + + navLeft BtnFiltersClear + navRight BtnServerSearch + } + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + InGamePlayerLabel + { + ControlName Label + auto_wide_tocontents 1 + tall 40 + labelText "#INGAME_PLAYERS" + xpos -250 + ypos 30 + visible 1 + zpos 101 + + font Default_28_ShadowGlow + bgcolor_override "0 0 0 120" + fgcolor_override "255 255 255 175" + visible 1 + + pin_to_sibling FilterPanel + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner BOTTOM_LEFT + } + + InGamePlayerCount + { + ControlName Label + auto_wide_tocontents 1 + tall 40 + labelText "" + font Default_28_ShadowGlow + bgcolor_override "0 0 0 120" + fgcolor_override "107 166 196 200" + xpos 6 + visible 1 + zpos 101 + + pin_to_sibling InGamePlayerLabel + pin_corner_to_sibling TOP_LEFT + pin_to_sibling_corner TOP_RIGHT + } + + FooterButtons { ControlName CNestedPanel diff --git a/Northstar.Client/mod/scripts/vscripts/sh_menu_models.gnut b/Northstar.Client/mod/scripts/vscripts/sh_menu_models.gnut index 58850e6a..cd663a06 100644 --- a/Northstar.Client/mod/scripts/vscripts/sh_menu_models.gnut +++ b/Northstar.Client/mod/scripts/vscripts/sh_menu_models.gnut @@ -2522,7 +2522,7 @@ while ( !clGlobal.initializedMenuModels ) WaitFrame() - + // setting menu camera while our viewentity isn't player will crash // unfortunately no way to close menu if we get our viewentity set while menu is open atm while ( GetViewEntity().GetClassName() == "class C_BaseEntity" ) @@ -2896,11 +2896,13 @@ { float screenScaleXModifier = 1920.0 / GetScreenSize()[0] // 1920 is base screen width float mouseXRotateDelta = deltaX * screenScaleXModifier * MOUSE_ROTATE_MULTIPLIER - //printt( "deltaX:", deltaX, "screenScaleModifier:", screenScaleModifier, "mouseRotateDelta:", mouseRotateDelta ) + //printt( "deltaX:", deltaX, "deltaY:", deltaY ) float screenScaleYModifier = 1080.0 / GetScreenSize()[1] // 1920 is base screen width float mouseYRotationDelta = deltaY * screenScaleYModifier * MOUSE_ROTATE_MULTIPLIER + UpdateMouseDeltaBuffer( deltaX, deltaY ) + RunMenuClientFunction( "UpdateMouseRotateDelta", mouseXRotateDelta, mouseYRotationDelta ) } #endif // UI diff --git a/Northstar.Client/mod/scripts/vscripts/ui/menu_ns_serverbrowser.nut b/Northstar.Client/mod/scripts/vscripts/ui/menu_ns_serverbrowser.nut index 19a544cc..dd2f8864 100644 --- a/Northstar.Client/mod/scripts/vscripts/ui/menu_ns_serverbrowser.nut +++ b/Northstar.Client/mod/scripts/vscripts/ui/menu_ns_serverbrowser.nut @@ -1,14 +1,132 @@ +untyped +// Only way to get Hud_GetPos(sliderButton) working was to use untyped + global function AddNorthstarServerBrowserMenu global function ThreadedAuthAndConnectToServer +global function UpdateMouseDeltaBuffer + +// Stop peeking + const int BUTTONS_PER_PAGE = 15 +const float DOUBLE_CLICK_TIME_MS = 0.2 // unsure what the ideal value is + struct { - int page = 0 - int lastSelectedServer = 0 + int deltaX = 0 + int deltaY = 0 +} mouseDeltaBuffer + +struct { + bool hideFull = false + bool hideEmpty = false + bool hideProtected = false + bool useSearch = false + string searchTerm + array<string> filterMaps + string filterMap + array<string> filterGamemodes + string filterGamemode +} filterArguments + +struct { + // true = alphabeticaly false = reverse + bool serverName = true + bool serverPlayers = true + bool serverMap = true + bool serverGamemode = true + bool serverLatency = true + // 0 = none; 1 = name; 2 = players; 3 = map; 5 = gamemode; 6 = latency + int sortingBy = 0 +} filterDirection + +struct serverStruct { + int serverIndex + bool serverProtected + string serverName + int serverPlayers + int serverPlayersMax + string serverMap + string serverGamemode + int serverLatency +} + +struct { + var menu + int lastSelectedServer = 999 + int focusedServerIndex = 0 + int scrollOffset = 0 bool serverListRequestFailed = false + float serverSelectedTime = 0 + float serverSelectedTimeLast = 0 + int serverButtonFocusedID = 0 + bool shouldFocus = true + bool cancelConnection = false + + array<serverStruct> serversArrayFiltered + + array<var> serverButtons + array<var> serversName + array<var> playerCountLabels + array<var> serversProtected + array<var> serversMap + array<var> serversGamemode + array<var> serversLatency } file + + +bool function floatCompareInRange(float arg1, float arg2, float tolerance) +{ + if ( arg1 > arg2 - tolerance && arg1 < arg2 + tolerance) return true + return false +} + + + +// Hard coded for now +array<string> function GetNorthstarGamemodes() +{ + array<string> modes + + //modes.append( "#PL_aitdm" ) + modes.append( "#PL_pilot_hunter" ) + modes.append( "#PL_hardpoint" ) + //modes.append( "#PL_attrition" ) + modes.append( "#PL_capture_the_flag" ) + modes.append( "#PL_last_titan_standing" ) + modes.append( "#PL_pilot_skirmish" ) + modes.append( "#PL_live_fire" ) + modes.append( "#PL_marked_for_death" ) + modes.append( "#PL_titan_brawl" ) + //modes.append( "#PL_fd_easy" ) + //modes.append( "#PL_fd_normal" ) + //modes.append( "#PL_fd_hard" ) + //modes.append( "#PL_fd_master" ) + //modes.append( "#PL_fd_insane" ) + modes.append( "#PL_ffa" ) + modes.append( "#PL_fra" ) + modes.append( "#PL_coliseum" ) + modes.append( "#PL_aegis_titan_brawl" ) + modes.append( "#PL_titan_brawl_turbo" ) + modes.append( "#PL_aegis_last_titan_standing" ) + modes.append( "#PL_turbo_last_titan_standing" ) + modes.append( "#PL_rocket_arena" ) + modes.append( "#PL_all_holopilot" ) + modes.append( "#PL_gg" ) + modes.append( "#PL_tt" ) + modes.append( "#PL_inf" ) + modes.append( "#PL_kr" ) + modes.append( "#PL_fastball" ) + modes.append( "#GAMEMODE_hs" ) + modes.append( "#GAMEMODE_ctf_comp" ) + + + return modes +} +//////////////////////////// +// Init +//////////////////////////// void function AddNorthstarServerBrowserMenu() { AddMenu( "ServerBrowserMenu", $"resource/ui/menus/server_browser.menu", InitServerBrowserMenu, "#MENU_SERVER_BROWSER" ) @@ -16,219 +134,795 @@ void function AddNorthstarServerBrowserMenu() void function InitServerBrowserMenu() { - var menu = GetMenu( "ServerBrowserMenu" ) + file.menu = GetMenu( "ServerBrowserMenu" ) + + // Get menu stuff + file.serverButtons = GetElementsByClassname( file.menu, "ServerButton" ) + file.serversName = GetElementsByClassname( file.menu, "ServerName" ) + file.playerCountLabels = GetElementsByClassname( file.menu, "PlayerCount" ) + file.serversProtected = GetElementsByClassname( file.menu, "ServerLock" ) + file.serversMap = GetElementsByClassname( file.menu, "ServerMap" ) + file.serversGamemode = GetElementsByClassname( file.menu, "ServerGamemode" ) + file.serversLatency = GetElementsByClassname( file.menu, "ServerLatency" ) + + // Create filter arrays + filterArguments.filterMaps.extend(GetPrivateMatchMaps()) + filterArguments.filterMaps.insert(0, "SWITCH_ANY") + filterArguments.filterMaps.append("mp_lobby") + + foreach ( int enum_, string map in filterArguments.filterMaps ) + Hud_DialogList_AddListItem( Hud_GetChild( file.menu, "SwtBtnSelectMap" ) , map, string( enum_ ) ) + + + filterArguments.filterGamemodes = GetNorthstarGamemodes() + filterArguments.filterGamemodes.insert(0, "SWITCH_ANY") + + // GetGameModeDisplayName( mode ) requires server talk even if it can be entirely client side + foreach ( int enum_, string mode in filterArguments.filterGamemodes ) + Hud_DialogList_AddListItem( Hud_GetChild( file.menu, "SwtBtnSelectGamemode" ) , mode, string( enum_ ) ) + + + // Event handlers + AddMenuEventHandler( file.menu, eUIEvent.MENU_CLOSE, OnCloseServerBrowserMenu ) + + + + AddMenuEventHandler( file.menu, eUIEvent.MENU_OPEN, OnServerBrowserMenuOpened ) + AddMenuFooterOption( file.menu, BUTTON_B, "#B_BUTTON_BACK", "#BACK" ) + AddMenuFooterOption( file.menu, BUTTON_Y, "#Y_REFRESH_SERVERS", "#REFRESH_SERVERS", RefreshServers ) + + // Setup server buttons + var width = 1120.0 * (GetScreenSize()[1] / 1080.0) + foreach ( var button in GetElementsByClassname( file.menu, "ServerButton" ) ) + { + AddButtonEventHandler( button, UIE_CLICK, OnServerButtonClicked ) + AddButtonEventHandler( button, UIE_GET_FOCUS, OnServerButtonFocused ) + Hud_SetWidth( button , width ) + } + + AddButtonEventHandler( Hud_GetChild( file.menu , "BtnServerDummmyTop" ), UIE_GET_FOCUS, OnHitDummyTop ) + AddButtonEventHandler( Hud_GetChild( file.menu , "BtnServerDummmyBottom" ), UIE_GET_FOCUS, OnHitDummyBottom ) + + + + AddButtonEventHandler( Hud_GetChild( file.menu, "BtnServerJoin"), UIE_CLICK, OnServerSelected ) + + AddButtonEventHandler( Hud_GetChild( file.menu, "BtnServerListUpArrow"), UIE_CLICK, OnUpArrowSelected ) + AddButtonEventHandler( Hud_GetChild( file.menu, "BtnServerListDownArrow"), UIE_CLICK, OnDownArrowSelected ) + AddButtonEventHandler( Hud_GetChild( file.menu, "BtnDummyAfterFilterClear"), UIE_GET_FOCUS, OnHitDummyAfterFilterClear ) + + + + AddButtonEventHandler( Hud_GetChild( file.menu, "BtnFiltersClear"), UIE_CLICK, OnBtnFiltersClear_Activate ) + + + AddButtonEventHandler( Hud_GetChild( file.menu, "BtnServerNameTab"), UIE_CLICK, SortServerListByName ) + AddButtonEventHandler( Hud_GetChild( file.menu, "BtnServerPlayersTab"), UIE_CLICK, SortServerListByPlayers ) + AddButtonEventHandler( Hud_GetChild( file.menu, "BtnServerMapTab"), UIE_CLICK, SortServerListByMap ) + AddButtonEventHandler( Hud_GetChild( file.menu, "BtnServerGamemodeTab"), UIE_CLICK, SortServerListByGamemode ) + AddButtonEventHandler( Hud_GetChild( file.menu, "BtnServerLatencyTab"), UIE_CLICK, SortServerListByLatency ) + + + AddButtonEventHandler( Hud_GetChild( file.menu, "SwtBtnSelectMap"), UIE_CHANGE, FilterAndUpdateList ) + AddButtonEventHandler( Hud_GetChild( file.menu, "SwtBtnSelectGamemode"), UIE_CHANGE, FilterAndUpdateList ) + AddButtonEventHandler( Hud_GetChild( file.menu, "SwtBtnHideFull"), UIE_CHANGE, FilterAndUpdateList ) + AddButtonEventHandler( Hud_GetChild( file.menu, "SwtBtnHideEmpty"), UIE_CHANGE, FilterAndUpdateList ) + AddButtonEventHandler( Hud_GetChild( file.menu, "SwtBtnHideProtected"), UIE_CHANGE, FilterAndUpdateList ) + AddButtonEventHandler( Hud_GetChild( file.menu, "BtnSearchLabel"), UIE_CHANGE, FilterAndUpdateList ) + + AddButtonEventHandler( Hud_GetChild( file.menu, "BtnServerSearch"), UIE_CHANGE, FilterAndUpdateList ) + + AddButtonEventHandler( Hud_GetChild( file.menu, "BtnServerDescription"), UIE_CLICK, ShowServerDescription ) + AddButtonEventHandler( Hud_GetChild( file.menu, "BtnServerMods"), UIE_CLICK, ShowServerMods ) + + AddButtonEventHandler( Hud_GetChild( file.menu, "ConnectingButton"), UIE_CLICK, ConnectingButton_Activate ) + + + // Hidden cause no need, if server descriptions become too long use this + Hud_SetEnabled( Hud_GetChild( file.menu, "BtnServerDescription"), false) + Hud_SetEnabled( Hud_GetChild( file.menu, "BtnServerMods"), false) + Hud_SetText( Hud_GetChild( file.menu, "BtnServerDescription"), "") + Hud_SetText( Hud_GetChild( file.menu, "BtnServerMods"), "") + + // Unfinished features + Hud_SetLocked( Hud_GetChild( file.menu, "BtnServerLatencyTab" ), true ) + + // Rui is a pain + RuiSetString( Hud_GetRui( Hud_GetChild( file.menu, "SwtBtnHideFull")), "buttonText", "") + RuiSetString( Hud_GetRui( Hud_GetChild( file.menu, "SwtBtnHideEmpty")), "buttonText", "") + RuiSetString( Hud_GetRui( Hud_GetChild( file.menu, "SwtBtnHideProtected")), "buttonText", "") + RuiSetString( Hud_GetRui( Hud_GetChild( file.menu, "SwtBtnSelectMap")), "buttonText", "") + RuiSetString( Hud_GetRui( Hud_GetChild( file.menu, "SwtBtnSelectGamemode")), "buttonText", "") + + + ToggleConnectingHUD(false) + + // UI was cut off on some aspect ratios; not perfect + UpdateServerInfoBasedOnRes() +} + +//////////////////////////// +// Slider +//////////////////////////// +void function UpdateMouseDeltaBuffer(int x, int y) +{ + mouseDeltaBuffer.deltaX += x + mouseDeltaBuffer.deltaY += y + + SliderBarUpdate() +} + +void function FlushMouseDeltaBuffer() +{ + mouseDeltaBuffer.deltaX = 0 + mouseDeltaBuffer.deltaY = 0 +} + + +void function SliderBarUpdate() +{ + if ( file.serversArrayFiltered.len() <= 15 ) + { + FlushMouseDeltaBuffer() + return + } + + var sliderButton = Hud_GetChild( file.menu , "BtnServerListSlider" ) + var sliderPanel = Hud_GetChild( file.menu , "BtnServerListSliderPanel" ) + var movementCapture = Hud_GetChild( file.menu , "MouseMovementCapture" ) + + Hud_SetFocused(sliderButton) + + float minYPos = -40.0 * (GetScreenSize()[1] / 1080.0) + float maxHeight = 562.0 * (GetScreenSize()[1] / 1080.0) + float maxYPos = minYPos - (maxHeight - Hud_GetHeight( sliderPanel )) + float useableSpace = (maxHeight - Hud_GetHeight( sliderPanel )) + + float jump = minYPos - (useableSpace / ( float( file.serversArrayFiltered.len()))) + + // got local from official respaw scripts, without untyped throws an error + local pos = Hud_GetPos(sliderButton)[1] + local newPos = pos - mouseDeltaBuffer.deltaY + FlushMouseDeltaBuffer() + + if ( newPos < maxYPos ) newPos = maxYPos + if ( newPos > minYPos ) newPos = minYPos + + Hud_SetPos( sliderButton , 2, newPos ) + Hud_SetPos( sliderPanel , 2, newPos ) + Hud_SetPos( movementCapture , 2, newPos ) + + file.scrollOffset = -int( ( (newPos - minYPos) / useableSpace ) * (file.serversArrayFiltered.len() - 15) ) + UpdateShownPage() +} + +void function UpdateListSliderHeight( float servers ) +{ + var sliderButton = Hud_GetChild( file.menu , "BtnServerListSlider" ) + var sliderPanel = Hud_GetChild( file.menu , "BtnServerListSliderPanel" ) + var movementCapture = Hud_GetChild( file.menu , "MouseMovementCapture" ) + + float maxHeight = 562.0 * (GetScreenSize()[1] / 1080.0) + + float height = maxHeight * (15.0 / servers ) + + if ( height > maxHeight ) height = maxHeight + + Hud_SetHeight( sliderButton , height ) + Hud_SetHeight( sliderPanel , height ) + Hud_SetHeight( movementCapture , height ) +} + + +void function UpdateListSliderPosition( int servers ) +{ + var sliderButton = Hud_GetChild( file.menu , "BtnServerListSlider" ) + var sliderPanel = Hud_GetChild( file.menu , "BtnServerListSliderPanel" ) + var movementCapture = Hud_GetChild( file.menu , "MouseMovementCapture" ) + + float minYPos = -40.0 * (GetScreenSize()[1] / 1080.0) + float useableSpace = (562.0 * (GetScreenSize()[1] / 1080.0) - Hud_GetHeight( sliderPanel )) + + float jump = minYPos - (useableSpace / ( float( servers ) - 15.0 ) * file.scrollOffset) - AddMenuEventHandler( menu, eUIEvent.MENU_OPEN, OnServerBrowserMenuOpened ) - AddMenuFooterOption( menu, BUTTON_B, "#B_BUTTON_BACK", "#BACK" ) - AddMenuFooterOption( menu, BUTTON_Y, "#Y_REFRESH_SERVERS", "#REFRESH_SERVERS", RefreshServers ) - AddMenuFooterOption( menu, BUTTON_SHOULDER_LEFT, "#PRIVATE_MATCH_PAGE_PREV", "#PRIVATE_MATCH_PAGE_PREV", CycleServersBack ) - AddMenuFooterOption( menu, BUTTON_SHOULDER_RIGHT, "#PRIVATE_MATCH_PAGE_NEXT", "#PRIVATE_MATCH_PAGE_NEXT", CycleServersForward ) - - foreach ( var button in GetElementsByClassname( GetMenu( "ServerBrowserMenu" ), "ServerButton" ) ) + //jump = jump * (GetScreenSize()[1] / 1080.0) + + if ( jump > minYPos ) jump = minYPos + + Hud_SetPos( sliderButton , 2, jump ) + Hud_SetPos( sliderPanel , 2, jump ) + Hud_SetPos( movementCapture , 2, jump ) +} + +void function OnScrollDown( var button ) +{ + if (file.serversArrayFiltered.len() <= 15) return + file.scrollOffset += 5 + if (file.scrollOffset + BUTTONS_PER_PAGE > file.serversArrayFiltered.len()) { + file.scrollOffset = file.serversArrayFiltered.len() - BUTTONS_PER_PAGE + } + UpdateShownPage() + UpdateListSliderPosition( file.serversArrayFiltered.len() ) +} + +void function OnScrollUp( var button ) +{ + file.scrollOffset -= 5 + if (file.scrollOffset < 0) { + file.scrollOffset = 0 + } + UpdateShownPage() + UpdateListSliderPosition( file.serversArrayFiltered.len() ) +} + +//////////////////////////// +// Connecting pop-up +//////////////////////////// +void function ToggleConnectingHUD( bool vis ) +{ + foreach (e in GetElementsByClassname(file.menu, "connectingHUD")) { + Hud_SetEnabled( e, vis ) + Hud_SetVisible( e, vis ) + } + + if ( vis ) Hud_SetFocused( Hud_GetChild( file.menu, "ConnectingButton" ) ) +} + +void function ConnectingButton_Activate( var button ) +{ + file.cancelConnection = true +} + +//////////////////////////// +// Aspect ratio compensation +//////////////////////////// +// No way to get aspect ratio sadly +// This doesn't werk on some obscure resolutions, mostly really small 4:3 +void function UpdateServerInfoBasedOnRes() +{ + if (floatCompareInRange(float(GetScreenSize()[0]) / float(GetScreenSize()[1]) , 1.6, 0.07)) // 16/10 + { + Hud_SetWidth( Hud_GetChild(file.menu, "ServerName"), 392) + Hud_SetWidth( Hud_GetChild(file.menu, "NextMapImage"), 400) + Hud_SetWidth( Hud_GetChild(file.menu, "NextMapBack"), 400) + Hud_SetWidth( Hud_GetChild(file.menu, "LabelMods"), 360) + Hud_SetWidth( Hud_GetChild(file.menu, "LabelDescription"), 360) + Hud_SetWidth( Hud_GetChild(file.menu, "ServerDetailsPanel"), 400) + } + if(floatCompareInRange(float(GetScreenSize()[0]) / float(GetScreenSize()[1]) , 1.3, 0.055)) // 4/3 { - AddButtonEventHandler( button, UIE_GET_FOCUS, OnServerFocused ) - AddButtonEventHandler( button, UIE_CLICK, OnServerSelected ) + Hud_SetWidth( Hud_GetChild(file.menu, "ServerName"), 292) + Hud_SetWidth( Hud_GetChild(file.menu, "NextMapImage"), 300) + Hud_SetWidth( Hud_GetChild(file.menu, "NextMapBack"), 300) + Hud_SetWidth( Hud_GetChild(file.menu, "LabelMods"), 260) + Hud_SetWidth( Hud_GetChild(file.menu, "LabelDescription"), 260) + Hud_SetWidth( Hud_GetChild(file.menu, "ServerDetailsPanel"), 300) } } +//////////////////////////// +// Open/close callbacks +//////////////////////////// +void function OnCloseServerBrowserMenu() +{ + DeregisterButtonPressedCallback(MOUSE_WHEEL_UP , OnScrollUp) + DeregisterButtonPressedCallback(MOUSE_WHEEL_DOWN , OnScrollDown) + DeregisterButtonPressedCallback(KEY_TAB , OnKeyTabPressed) +} + void function OnServerBrowserMenuOpened() { - Hud_SetText( Hud_GetChild( GetMenu( "ServerBrowserMenu" ), "Title" ), "#MENU_TITLE_SERVER_BROWSER" ) + Hud_SetText( Hud_GetChild( file.menu, "Title" ), "#MENU_TITLE_SERVER_BROWSER" ) UI_SetPresentationType( ePresentationType.KNOWLEDGEBASE_MAIN ) - - file.page = 0 + + file.scrollOffset = 0 // dont rerequest if we came from the connect menu if ( !NSIsRequestingServerList() && uiGlobal.lastMenuNavDirection != MENU_NAV_BACK ) { NSClearRecievedServerList() NSRequestServerList() } - + thread WaitForServerListRequest() + + + RegisterButtonPressedCallback(MOUSE_WHEEL_UP , OnScrollUp) + RegisterButtonPressedCallback(MOUSE_WHEEL_DOWN , OnScrollDown) + RegisterButtonPressedCallback(KEY_TAB , OnKeyTabPressed) } -void function RefreshServers( var button ) +//////////////////////////// +// Arrow navigation fuckery +//////////////////////////// +bool function IsFilterPanelElementFocused() { + // get name of focused element + var focusedElement = GetFocus(); + var name = Hud_GetHudName(focusedElement); + + print(name) + + // kinda sucks but just check if any of the filter elements + // has focus. would be nice to have tags or sth here + bool match = (name == "FilterPanel") || + (name == "BtnSearchLabel") || + (name == "BtnServerSearch") || + (name == "SwtBtnSelectMap") || + (name == "SwtBtnSelectGamemode") || + (name == "SwtBtnHideFull") || + (name == "SwtBtnHideEmpty") || + (name == "SwtBtnHideProtected") || + (name == "BtnFiltersClear") || + (name == "BtnDummyAfterFilterClear"); + + + return match; +} + +void function OnKeyTabPressed(var button) { + // toggle focus between server list and filter panel + if (IsFilterPanelElementFocused()) { + // print("Switching focus from filter panel to server list") + Hud_SetFocused(Hud_GetChild(file.menu, "BtnServer1")) + } + else { + // print("Switching focus from server list to filter panel") + Hud_SetFocused(Hud_GetChild(file.menu, "BtnServerSearch")) + HideServerInfo() + } +} + +void function OnHitDummyTop(var button) { + file.scrollOffset -= 1 + if (file.scrollOffset < 0) { + // was at top already + file.scrollOffset = 0 + Hud_SetFocused(Hud_GetChild(file.menu, "BtnServerNameTab")) + } else { + // only update if list position changed + UpdateShownPage() + UpdateListSliderPosition( file.serversArrayFiltered.len() ) + DisplayFocusedServerInfo(file.serverButtonFocusedID) + Hud_SetFocused(Hud_GetChild(file.menu, "BtnServer1")) + } +} + +void function OnHitDummyBottom(var button) { + file.scrollOffset += 1 + if (file.scrollOffset + BUTTONS_PER_PAGE > file.serversArrayFiltered.len()) + { + // was at bottom already + file.scrollOffset = file.serversArrayFiltered.len() - BUTTONS_PER_PAGE + Hud_SetFocused(Hud_GetChild(file.menu, "BtnServerSearch")) + } else { + // only update if list position changed + UpdateShownPage() + UpdateListSliderPosition( file.serversArrayFiltered.len() ) + DisplayFocusedServerInfo(file.serverButtonFocusedID) + Hud_SetFocused(Hud_GetChild(file.menu, "BtnServer15")) + } +} + +void function OnHitDummyAfterFilterClear(var button) { + Hud_SetFocused(Hud_GetChild(file.menu, "BtnServer1")) +} + + +void function OnDownArrowSelected( var button ) { - if ( NSIsRequestingServerList() ) - return - - file.page = 0 - file.serverListRequestFailed = false - NSClearRecievedServerList() - NSRequestServerList() - - thread WaitForServerListRequest() + if (file.serversArrayFiltered.len() <= 15) return + file.scrollOffset += 1 + if (file.scrollOffset + BUTTONS_PER_PAGE > file.serversArrayFiltered.len()) { + file.scrollOffset = file.serversArrayFiltered.len() - BUTTONS_PER_PAGE + } + UpdateShownPage() + UpdateListSliderPosition( file.serversArrayFiltered.len() ) } -void function CycleServersBack( var button ) + +void function OnUpArrowSelected( var button ) { - if ( file.page == 0 ) - return - - file.page-- + file.scrollOffset -= 1 + if (file.scrollOffset < 0) { + file.scrollOffset = 0 + } UpdateShownPage() + UpdateListSliderPosition( file.serversArrayFiltered.len() ) +} + + +//////////////////////////// +// Unused +//////////////////////////// +void function ShowServerDescription( var button ) +{ + Hud_SetVisible( Hud_GetChild( file.menu, "LabelDescription"), true) + Hud_SetVisible( Hud_GetChild( file.menu, "LabelMods"), false) +} + +void function ShowServerMods( var button ) +{ + Hud_SetVisible( Hud_GetChild( file.menu, "LabelDescription"), false) + Hud_SetVisible( Hud_GetChild( file.menu, "LabelMods"), true) +} + +//////////////////////////// +// Server list; filter,update,... +//////////////////////////// +void function HideServerInfo() { + Hud_SetVisible(Hud_GetChild(file.menu, "BtnServerDescription"), false) + Hud_SetVisible(Hud_GetChild(file.menu, "BtnServerMods"), false) + Hud_SetVisible(Hud_GetChild(file.menu, "BtnServerJoin"), false) + Hud_SetVisible(Hud_GetChild(file.menu, "LabelDescription"), false) + Hud_SetVisible(Hud_GetChild(file.menu, "LabelMods"), false) + Hud_SetVisible(Hud_GetChild(file.menu, "NextMapImage"), false) + Hud_SetVisible(Hud_GetChild(file.menu, "NextMapBack"), false) + Hud_SetVisible(Hud_GetChild(file.menu, "NextMapName"), false) + Hud_SetVisible(Hud_GetChild(file.menu, "ServerName"), false) + Hud_SetVisible(Hud_GetChild(file.menu, "NextModeIcon"), false) + Hud_SetVisible(Hud_GetChild(file.menu, "NextGameModeName"), false) } -void function CycleServersForward( var button ) +void function OnBtnFiltersClear_Activate( var button ) { - if ( ( file.page + 1 ) * BUTTONS_PER_PAGE >= NSGetServerCount() ) + Hud_SetText( Hud_GetChild( file.menu, "BtnServerSearch" ), "" ) + + SetConVarBool( "filter_hide_empty", false ) + SetConVarBool( "filter_hide_full", false ) + SetConVarBool( "filter_hide_protected", false ) + SetConVarInt( "filter_map", 0 ) + SetConVarInt( "filter_gamemode", 0 ) + + FilterAndUpdateList(0) +} + +void function FilterAndUpdateList( var n ) +{ + filterArguments.searchTerm = Hud_GetUTF8Text( Hud_GetChild( file.menu, "BtnServerSearch" ) ) + if ( filterArguments.searchTerm == "" ) filterArguments.useSearch = false else filterArguments.useSearch = true + filterArguments.filterMap = filterArguments.filterMaps[ GetConVarInt( "filter_map" ) ] + filterArguments.filterGamemode = filterArguments.filterGamemodes[ GetConVarInt( "filter_gamemode" ) ] + filterArguments.hideEmpty = GetConVarBool( "filter_hide_empty" ) + filterArguments.hideFull = GetConVarBool( "filter_hide_full" ) + filterArguments.hideProtected = GetConVarBool( "filter_hide_protected" ) + + file.scrollOffset = 0 + UpdateListSliderPosition( file.serversArrayFiltered.len() ) + + FilterServerList() + + switch ( filterDirection.sortingBy ) + { + case 0: + UpdateShownPage() + break + case 1: + filterDirection.serverName = !filterDirection.serverName + SortServerListByName(0) + break + case 2: + filterDirection.serverPlayers = !filterDirection.serverPlayers + SortServerListByPlayers(0) + break + case 3: + filterDirection.serverMap = !filterDirection.serverMap + SortServerListByMap(0) + break + case 5: // 4 skipped cause it doesn't work respawn pls fix + filterDirection.serverGamemode = !filterDirection.serverGamemode + SortServerListByGamemode(0) + break + case 6: + filterDirection.serverLatency = !filterDirection.serverLatency + SortServerListByLatency(0) + break + default: + printt( "How the f did you get here" ) + } + + if ( file.shouldFocus ) + { + file.shouldFocus = false + Hud_SetFocused( Hud_GetChild( file.menu, "BtnServer1" ) ) + } +} + + +void function RefreshServers( var button ) +{ + if ( NSIsRequestingServerList() ) return - - file.page++ - UpdateShownPage() + + file.serverListRequestFailed = false + file.scrollOffset = 0 + NSClearRecievedServerList() + NSRequestServerList() + + thread WaitForServerListRequest() } + void function WaitForServerListRequest() { - var menu = GetMenu( "ServerBrowserMenu" ) - array<var> serverButtons = GetElementsByClassname( menu, "ServerButton" ) - foreach ( var button in serverButtons ) + + for ( int i = 0; i < 15; i++) { - Hud_SetEnabled( button, false ) - Hud_SetVisible( button, false ) - } - - Hud_SetVisible( Hud_GetChild( menu, "LabelDetails" ), false ) - Hud_SetVisible( Hud_GetChild( menu, "NextMapImage" ), false ) - Hud_SetVisible( Hud_GetChild( menu, "NextMapName" ), false ) - Hud_SetVisible( Hud_GetChild( menu, "NextModeIcon" ), false ) - Hud_SetVisible( Hud_GetChild( menu, "NextGameModeName" ), false ) - - Hud_SetEnabled( serverButtons[ 0 ], true ) - Hud_SetVisible( serverButtons[ 0 ], true ) - - SetButtonRuiText( serverButtons[ 0 ], "#NS_SERVERBROWSER_WAITINGFORSERVERS" ) - + Hud_SetVisible( file.serversProtected[ i ], false ) + Hud_SetVisible( file.serverButtons[ i ], false ) + Hud_SetText( file.serversName[ i ], "" ) + Hud_SetText( file.playerCountLabels[ i ], "" ) + Hud_SetText( file.serversMap[ i ], "" ) + Hud_SetText( file.serversGamemode[ i ], "" ) + Hud_SetText( file.serversLatency[ i ], "" ) + } + + + HideServerInfo() + + + Hud_SetVisible( file.serversName[ 0 ], true ) + + Hud_SetText( file.serversName[ 0 ], "#NS_SERVERBROWSER_WAITINGFORSERVERS" ) + // wait for request to complete while ( NSIsRequestingServerList() ) WaitFrame() - + file.serverListRequestFailed = !NSMasterServerConnectionSuccessful() if ( file.serverListRequestFailed ) - SetButtonRuiText( serverButtons[ 0 ], "#NS_SERVERBROWSER_CONNECTIONFAILED" ) + { + Hud_SetText( file.serversName[ 0 ], "#NS_SERVERBROWSER_CONNECTIONFAILED" ) + } else - UpdateShownPage() + { + FilterAndUpdateList(0) + } } -void function UpdateShownPage() + + +void function FilterServerList() { - var menu = GetMenu( "ServerBrowserMenu" ) + file.serversArrayFiltered.clear() + int totalPlayers = 0 - // hide old ui elements - array<var> serverButtons = GetElementsByClassname( menu, "ServerButton" ) - foreach ( var button in serverButtons ) + for ( int i = 0; i < NSGetServerCount(); i++ ) { - Hud_SetEnabled( button, false ) - Hud_SetVisible( button, false ) - } - - Hud_SetFocused( serverButtons[ serverButtons.len() - 1 ] ) - - Hud_SetVisible( Hud_GetChild( menu, "LabelDetails" ), false ) - Hud_SetVisible( Hud_GetChild( menu, "NextMapImage" ), false ) - Hud_SetVisible( Hud_GetChild( menu, "NextMapName" ), false ) - Hud_SetVisible( Hud_GetChild( menu, "NextModeIcon" ), false ) - Hud_SetVisible( Hud_GetChild( menu, "NextGameModeName" ), false ) - - if ( NSGetServerCount() == 0 ) + serverStruct tempServer + tempServer.serverIndex = i + tempServer.serverProtected = NSServerRequiresPassword( i ) + tempServer.serverName = NSGetServerName( i ) + tempServer.serverPlayers = NSGetServerPlayerCount( i ) + tempServer.serverPlayersMax = NSGetServerMaxPlayerCount( i ) + tempServer.serverMap = NSGetServerMap( i ) + tempServer.serverGamemode = GetGameModeDisplayName( NSGetServerPlaylist ( i ) ) + + totalPlayers += tempServer.serverPlayers + + + // Branchless programming ;) + if (!(filterArguments.hideEmpty && tempServer.serverPlayers == 0) && !(filterArguments.hideFull && tempServer.serverPlayers == tempServer.serverPlayersMax) && !(filterArguments.hideProtected && tempServer.serverProtected)) + { + if ( filterArguments.useSearch ) + { + string sName = tempServer.serverName.tolower() + string sTerm = filterArguments.searchTerm.tolower() + + if ( sName.find(sTerm) != null) + { + if (filterArguments.filterMap != "SWITCH_ANY" && filterArguments.filterMap == tempServer.serverMap) + { + CheckGamemode( tempServer ) + } + else if (filterArguments.filterMap == "SWITCH_ANY") + { + CheckGamemode( tempServer ) + } + } + } + else + { + if (filterArguments.filterMap != "SWITCH_ANY" && filterArguments.filterMap == tempServer.serverMap) + { + CheckGamemode( tempServer ) + } + else if (filterArguments.filterMap == "SWITCH_ANY") + { + CheckGamemode( tempServer ) + } + } + } + } + + + printt("Better.Serverbrowser:------------------------") + printt("Server count: ", NSGetServerCount()) + printt("Filtered count: ", file.serversArrayFiltered.len()) + printt("Total players: ", totalPlayers) + printt("This message gets shown only on full refresh") + printt("---------------------------------------------") + + Hud_SetText( Hud_GetChild( file.menu, "InGamePlayerCount" ), string( totalPlayers ) ) +} + +void function CheckGamemode( serverStruct t ) +{ + if (filterArguments.filterGamemode != "SWITCH_ANY" && filterArguments.filterGamemode == t.serverGamemode) { - Hud_SetEnabled( serverButtons[ 0 ], true ) - Hud_SetVisible( serverButtons[ 0 ], true ) - SetButtonRuiText( serverButtons[ 0 ], "#NS_SERVERBROWSER_NOSERVERS" ) - return + file.serversArrayFiltered.append( t ) } - - // this trycatch likely isn't necessary, but i can't test whether this'll error on higher pagecounts and want to go sleep - try + else if (filterArguments.filterGamemode == "SWITCH_ANY") { - for ( int i = 0; ( file.page * BUTTONS_PER_PAGE ) + i < NSGetServerCount() && i < serverButtons.len(); i++ ) + file.serversArrayFiltered.append( t ) + } +} + + +void function UpdateShownPage() +{ + + for ( int i = 0; i < 15; i++) { - int serverIndex = ( file.page * BUTTONS_PER_PAGE ) + i - - Hud_SetEnabled( serverButtons[ i ], true ) - Hud_SetVisible( serverButtons[ i ], true ) - SetButtonRuiText( serverButtons[ i ], NSGetServerName( serverIndex ) ) + Hud_SetVisible( file.serversProtected[ i ], false ) + Hud_SetVisible( file.serverButtons[ i ], false ) + Hud_SetText( file.serversName[ i ], "" ) + Hud_SetText( file.playerCountLabels[ i ], "" ) + Hud_SetText( file.serversMap[ i ], "" ) + Hud_SetText( file.serversGamemode[ i ], "" ) + Hud_SetText( file.serversLatency[ i ], "" ) } + + int j = file.serversArrayFiltered.len() > 15 ? 15 : file.serversArrayFiltered.len() + + for ( int i = 0; i < j; i++ ) + { + + int buttonIndex = file.scrollOffset + i + int serverIndex = file.serversArrayFiltered[ buttonIndex ].serverIndex + + Hud_SetEnabled( file.serverButtons[ i ], true ) + Hud_SetVisible( file.serverButtons[ i ], true ) + + Hud_SetVisible( file.serversProtected[ i ], file.serversArrayFiltered[ buttonIndex ].serverProtected ) + Hud_SetText( file.serversName[ i ], file.serversArrayFiltered[ buttonIndex ].serverName ) + Hud_SetText( file.playerCountLabels[ i ], format( "%i/%i", file.serversArrayFiltered[ buttonIndex ].serverPlayers, file.serversArrayFiltered[ buttonIndex ].serverPlayersMax ) ) + Hud_SetText( file.serversMap[ i ], GetMapDisplayName( file.serversArrayFiltered[ buttonIndex ].serverMap ) ) + Hud_SetText( file.serversGamemode[ i ], file.serversArrayFiltered[ buttonIndex ].serverGamemode ) } - catch(ex) {} + + + if ( NSGetServerCount() == 0 ) + { + Hud_SetEnabled( file.serverButtons[ 0 ], true ) + Hud_SetVisible( file.serverButtons[ 0 ], true ) + Hud_SetText( file.serversName[ 0 ], "#NS_SERVERBROWSER_NOSERVERS" ) + } + UpdateListSliderHeight( float( file.serversArrayFiltered.len() ) ) +} + +void function OnServerButtonFocused( var button ) +{ + int scriptID = int (Hud_GetScriptID(button)) + file.serverButtonFocusedID = scriptID + DisplayFocusedServerInfo(scriptID); + } -void function OnServerFocused( var button ) +void function OnServerButtonClicked(var button) { + int scriptID = int (Hud_GetScriptID(button)) + + DisplayFocusedServerInfo(scriptID) + CheckDoubleClick(scriptID, true) +} + +void function CheckDoubleClick(int scriptID, bool wasClickNav) +{ + file.focusedServerIndex = file.serversArrayFiltered[ file.scrollOffset + scriptID ].serverIndex + int serverIndex = file.scrollOffset + scriptID + + bool sameServer = false + if (file.lastSelectedServer == serverIndex) sameServer = true + + + file.serverSelectedTimeLast = file.serverSelectedTime + file.serverSelectedTime = Time() + + printt(file.serverSelectedTime - file.serverSelectedTimeLast, file.lastSelectedServer, serverIndex) + + file.lastSelectedServer = serverIndex + + + if (wasClickNav && (file.serverSelectedTime - file.serverSelectedTimeLast < DOUBLE_CLICK_TIME_MS) && sameServer) + { + OnServerSelected(0) + } +} + +void function DisplayFocusedServerInfo( int scriptID) +{ + if (scriptID == 999 || scriptID == -1 || scriptID == 16) return + if ( NSIsRequestingServerList() || NSGetServerCount() == 0 || file.serverListRequestFailed ) return var menu = GetMenu( "ServerBrowserMenu" ) - int serverIndex = file.page * BUTTONS_PER_PAGE + int ( Hud_GetScriptID( button ) ) - // text panel - Hud_SetVisible( Hud_GetChild( menu, "LabelDetails" ), true ) - var textRui = Hud_GetRui( Hud_GetChild( menu, "LabelDetails" ) ) - RuiSetGameTime( textRui, "startTime", -99999.99 ) // make sure it skips the whole animation for showing this - RuiSetString( textRui, "messageText", FormatServerDescription( serverIndex ) ) + int serverIndex = file.scrollOffset + scriptID - // map name/image - string map = NSGetServerMap( serverIndex ) + + Hud_SetVisible( Hud_GetChild( menu, "BtnServerDescription" ), true ) + Hud_SetVisible( Hud_GetChild( menu, "BtnServerMods" ), true ) + Hud_SetVisible( Hud_GetChild( menu, "BtnServerJoin" ), true ) + // text panels + Hud_SetVisible( Hud_GetChild( menu, "LabelDescription" ), true ) + Hud_SetVisible( Hud_GetChild( menu, "LabelMods" ), false ) + //RuiSetGameTime( textRui, "startTime", -99999.99 ) // make sure it skips the whole animation for showing this + Hud_SetText( Hud_GetChild( menu, "LabelDescription" ), NSGetServerDescription( file.serversArrayFiltered[ serverIndex ].serverIndex ) + "\n\nRequired Mods:\n" + FillInServerModsLabel( file.serversArrayFiltered[ serverIndex ].serverIndex )) + //Hud_SetText( Hud_GetChild( menu, "LabelMods" ), FillInServerModsLabel( file.serversArrayFiltered[ serverIndex ].serverIndex ) ) + + // map name/image/server name + string map = file.serversArrayFiltered[ serverIndex ].serverMap Hud_SetVisible( Hud_GetChild( menu, "NextMapImage" ), true ) + Hud_SetVisible( Hud_GetChild( menu, "NextMapBack" ), true ) RuiSetImage( Hud_GetRui( Hud_GetChild( menu, "NextMapImage" ) ), "basicImage", GetMapImageForMapName( map ) ) Hud_SetVisible( Hud_GetChild( menu, "NextMapName" ), true ) Hud_SetText( Hud_GetChild( menu, "NextMapName" ), GetMapDisplayName( map ) ) + Hud_SetVisible( Hud_GetChild( menu, "ServerName" ), true ) + Hud_SetText( Hud_GetChild( menu, "ServerName" ), NSGetServerName( file.serversArrayFiltered[ serverIndex ].serverIndex ) ) // mode name/image - string mode = NSGetServerPlaylist( serverIndex ) + string mode = file.serversArrayFiltered[ serverIndex ].serverGamemode Hud_SetVisible( Hud_GetChild( menu, "NextModeIcon" ), true ) RuiSetImage( Hud_GetRui( Hud_GetChild( menu, "NextModeIcon" ) ), "basicImage", GetPlaylistThumbnailImage( mode ) ) Hud_SetVisible( Hud_GetChild( menu, "NextGameModeName" ), true ) - - string displayName = GetGameModeDisplayName( mode ) - if ( displayName.len() != 0 ) - Hud_SetText( Hud_GetChild( menu, "NextGameModeName" ), displayName ) + + if ( mode.len() != 0 ) + Hud_SetText( Hud_GetChild( menu, "NextGameModeName" ), mode ) else Hud_SetText( Hud_GetChild( menu, "NextGameModeName" ), "#NS_SERVERBROWSER_UNKNOWNMODE" ) } -string function FormatServerDescription( int server ) +string function FillInServerModsLabel( int server ) { - string ret = "\n\n\n\n" - - ret += NSGetServerName( server ) + "\n" - ret += format( "%i/%i players\n", NSGetServerPlayerCount( server ), NSGetServerMaxPlayerCount( server ) ) - ret += NSGetServerDescription( server ) + "\n\n" - - ret += "Required Mods: \n" + string ret + for ( int i = 0; i < NSGetServerRequiredModsCount( server ); i++ ) - ret += " " + NSGetServerRequiredModName( server, i ) + " v" + NSGetServerRequiredModVersion( server, i ) + "\n" - + { + ret += " " + ret += NSGetServerRequiredModName( server, i ) + " v" + NSGetServerRequiredModVersion( server, i ) + "\n" + } return ret } + void function OnServerSelected( var button ) { if ( NSIsRequestingServerList() || NSGetServerCount() == 0 || file.serverListRequestFailed ) return - int serverIndex = file.page * BUTTONS_PER_PAGE + int ( Hud_GetScriptID( button ) ) + int serverIndex = file.focusedServerIndex + file.lastSelectedServer = serverIndex // check mods - for ( int i = 0; i < NSGetServerRequiredModsCount( serverIndex ); i++ ) - { + for ( int i = 0; i < NSGetServerRequiredModsCount( serverIndex ); i++ ) + { if ( !NSGetModNames().contains( NSGetServerRequiredModName( serverIndex, i ) ) ) - { + { DialogData dialogData dialogData.header = "#ERROR" dialogData.message = "Missing mod \"" + NSGetServerRequiredModName( serverIndex, i ) + "\" v" + NSGetServerRequiredModVersion( serverIndex, i ) dialogData.image = $"ui/menu/common/dialog_error" - + #if PC_PROG AddDialogButton( dialogData, "#DISMISS" ) - + AddDialogFooter( dialogData, "#A_BUTTON_SELECT" ) #endif // PC_PROG AddDialogFooter( dialogData, "#B_BUTTON_DISMISS_RUI" ) - + OpenDialog( dialogData ) - + return } else @@ -236,7 +930,7 @@ void function OnServerSelected( var button ) // this uses semver https://semver.org array<string> serverModVersion = split( NSGetServerRequiredModVersion( serverIndex, i ), "." ) array<string> clientModVersion = split( NSGetModVersionByModName( NSGetServerRequiredModName( serverIndex, i ) ), "." ) - + bool semverFail = false // if server has invalid semver don't bother checking if ( serverModVersion.len() == 3 ) @@ -248,34 +942,38 @@ void function OnServerSelected( var button ) else if ( clientModVersion[ 0 ] != serverModVersion[ 0 ] ) semverFail = true } - + if ( semverFail ) { DialogData dialogData dialogData.header = "#ERROR" - dialogData.message = "Server has mod \"" + NSGetServerRequiredModName( serverIndex, i ) + "\" v" + NSGetServerRequiredModVersion( serverIndex, i ) + " while we have v" + NSGetModVersionByModName( NSGetServerRequiredModName( serverIndex, i ) ) + dialogData.message = "Server has mod \"" + NSGetServerRequiredModName( serverIndex, i ) + "\" v" + NSGetServerRequiredModVersion( serverIndex, i ) + " while we have v" + NSGetModVersionByModName( NSGetServerRequiredModName( serverIndex, i ) ) dialogData.image = $"ui/menu/common/dialog_error" - + #if PC_PROG AddDialogButton( dialogData, "#DISMISS" ) - + AddDialogFooter( dialogData, "#A_BUTTON_SELECT" ) #endif // PC_PROG AddDialogFooter( dialogData, "#B_BUTTON_DISMISS_RUI" ) - + OpenDialog( dialogData ) - + return } } } - + if ( NSServerRequiresPassword( serverIndex ) ) + { + OnCloseServerBrowserMenu() AdvanceMenu( GetMenu( "ConnectWithPasswordMenu" ) ) + } else thread ThreadedAuthAndConnectToServer() } + void function ThreadedAuthAndConnectToServer( string password = "" ) { if ( NSIsAuthenticatingWithServer() ) @@ -283,18 +981,32 @@ void function ThreadedAuthAndConnectToServer( string password = "" ) print( "trying to authenticate with server " + NSGetServerName( file.lastSelectedServer ) + " with password " + password ) NSTryAuthWithServer( file.lastSelectedServer, password ) - - while ( NSIsAuthenticatingWithServer() ) + + ToggleConnectingHUD( true ) + + while ( NSIsAuthenticatingWithServer() && !file.cancelConnection) + { WaitFrame() - + } + + ToggleConnectingHUD( false ) + + if (file.cancelConnection) + { + file.cancelConnection = false + return + } + + file.cancelConnection = false + if ( NSWasAuthSuccessful() ) { bool modsChanged - + array<string> requiredMods for ( int i = 0; i < NSGetServerRequiredModsCount( file.lastSelectedServer ); i++ ) requiredMods.append( NSGetServerRequiredModName( file.lastSelectedServer, i ) ) - + // unload mods we don't need, load necessary ones and reload mods before connecting foreach ( string mod in NSGetModNames() ) { @@ -304,27 +1016,159 @@ void function ThreadedAuthAndConnectToServer( string password = "" ) NSSetModEnabled( mod, requiredMods.contains( mod ) ) } } - + // only actually reload if we need to since the uiscript reset on reload lags hard if ( modsChanged ) ReloadMods() - NSConnectToAuthedServer() } else - { + { DialogData dialogData dialogData.header = "#ERROR" dialogData.message = "Authentication Failed" dialogData.image = $"ui/menu/common/dialog_error" - + #if PC_PROG AddDialogButton( dialogData, "#DISMISS" ) - + AddDialogFooter( dialogData, "#A_BUTTON_SELECT" ) #endif // PC_PROG AddDialogFooter( dialogData, "#B_BUTTON_DISMISS_RUI" ) OpenDialog( dialogData ) } -}
\ No newline at end of file +} + +////////////////////////////////////// +// Shadow realm +////////////////////////////////////// +void function SortServerListByName( var button ) +{ + filterDirection.sortingBy = 1 + + int n = file.serversArrayFiltered.len() - 1 + + serverStruct tempServer + + for ( int i = 0; i < n; i++) + { + for ( int j = 0; j < n - 1; j++) + { + if ( file.serversArrayFiltered[ j ].serverName < file.serversArrayFiltered[ j + 1 ].serverName && filterDirection.serverName || file.serversArrayFiltered[ j ].serverName > file.serversArrayFiltered[ j + 1 ].serverName && !filterDirection.serverName) + { + tempServer = file.serversArrayFiltered[ j ] + file.serversArrayFiltered[ j ] = file.serversArrayFiltered[ j + 1 ] + file.serversArrayFiltered[ j + 1 ] = tempServer + } + } + } + + filterDirection.serverName = !filterDirection.serverName + + UpdateShownPage() +} + +void function SortServerListByPlayers( var button ) +{ + filterDirection.sortingBy = 2 + + int n = file.serversArrayFiltered.len() - 1 + + serverStruct tempServer + + for ( int i = 0; i < n; i++) + { + for ( int j = 0; j < n - 1; j++) + { + if ( file.serversArrayFiltered[ j ].serverPlayers < file.serversArrayFiltered[ j + 1 ].serverPlayers && filterDirection.serverPlayers || file.serversArrayFiltered[ j ].serverPlayers > file.serversArrayFiltered[ j + 1 ].serverPlayers && !filterDirection.serverPlayers) + { + tempServer = file.serversArrayFiltered[ j ] + file.serversArrayFiltered[ j ] = file.serversArrayFiltered[ j + 1 ] + file.serversArrayFiltered[ j + 1 ] = tempServer + } + } + } + + filterDirection.serverPlayers = !filterDirection.serverPlayers + + UpdateShownPage() +} + +void function SortServerListByMap( var button ) +{ + filterDirection.sortingBy = 3 + + int n = file.serversArrayFiltered.len() - 1 + + serverStruct tempServer + + for ( int i = 0; i < n; i++) + { + for ( int j = 0; j < n - 1; j++) + { + if ( Localize(file.serversArrayFiltered[ j ].serverMap) < Localize(file.serversArrayFiltered[ j + 1 ].serverMap) && filterDirection.serverMap || Localize(file.serversArrayFiltered[ j ].serverMap) > Localize(file.serversArrayFiltered[ j + 1 ].serverMap) && !filterDirection.serverMap) + { + tempServer = file.serversArrayFiltered[ j ] + file.serversArrayFiltered[ j ] = file.serversArrayFiltered[ j + 1 ] + file.serversArrayFiltered[ j + 1 ] = tempServer + } + } + } + + filterDirection.serverMap = !filterDirection.serverMap + + UpdateShownPage() +} + +void function SortServerListByGamemode( var button ) +{ + filterDirection.sortingBy = 5 + + int n = file.serversArrayFiltered.len() - 1 + + serverStruct tempServer + + for ( int i = 0; i < n; i++) + { + for ( int j = 0; j < n - 1; j++) + { + if ( Localize(file.serversArrayFiltered[ j ].serverGamemode) < Localize(file.serversArrayFiltered[ j + 1 ].serverGamemode) && filterDirection.serverGamemode || Localize(file.serversArrayFiltered[ j ].serverGamemode) > Localize(file.serversArrayFiltered[ j + 1 ].serverGamemode) && !filterDirection.serverGamemode) + { + tempServer = file.serversArrayFiltered[ j ] + file.serversArrayFiltered[ j ] = file.serversArrayFiltered[ j + 1 ] + file.serversArrayFiltered[ j + 1 ] = tempServer + } + } + } + + filterDirection.serverGamemode = !filterDirection.serverGamemode + + UpdateShownPage() +} + +void function SortServerListByLatency( var button ) +{ + filterDirection.sortingBy = 5 + + int n = file.serversArrayFiltered.len() - 1 + + serverStruct tempServer + + for ( int i = 0; i < n; i++) + { + for ( int j = 0; j < n - 1; j++) + { + if ( file.serversArrayFiltered[ j ].serverLatency < file.serversArrayFiltered[ j + 1 ].serverLatency && filterDirection.serverLatency || file.serversArrayFiltered[ j ].serverLatency > file.serversArrayFiltered[ j + 1 ].serverLatency && !filterDirection.serverLatency) + { + tempServer = file.serversArrayFiltered[ j ] + file.serversArrayFiltered[ j ] = file.serversArrayFiltered[ j + 1 ] + file.serversArrayFiltered[ j + 1 ] = tempServer + } + } + } + + filterDirection.serverLatency = !filterDirection.serverLatency + + UpdateShownPage() +} diff --git a/Northstar.Client/mod/scripts/vscripts/ui/panel_mainmenu.nut b/Northstar.Client/mod/scripts/vscripts/ui/panel_mainmenu.nut index 25097c6f..ecb34d47 100644 --- a/Northstar.Client/mod/scripts/vscripts/ui/panel_mainmenu.nut +++ b/Northstar.Client/mod/scripts/vscripts/ui/panel_mainmenu.nut @@ -502,9 +502,9 @@ void function TryUnlockNorthstarButton() // unlock "Launch Northstar" button until you're authed with masterserver, are allowing insecure auth, or 7.5 seconds have passed float time = Time() - while ( Time() < time + 7.5 || GetConVarInt( "ns_has_agreed_to_send_token" ) != NS_AGREED_TO_SEND_TOKEN ) + while ( GetConVarInt( "ns_has_agreed_to_send_token" ) != NS_AGREED_TO_SEND_TOKEN ) { - if ( NSIsMasterServerAuthenticated() || GetConVarBool( "ns_auth_allow_insecure" ) ) + if ( ( NSIsMasterServerAuthenticated() && IsStryderAllowingMP() ) || GetConVarBool( "ns_auth_allow_insecure" ) ) break WaitFrame() diff --git a/Northstar.Custom/mod/scripts/vscripts/gamemodes/_gamemode_fastball_intro.gnut b/Northstar.Custom/mod/scripts/vscripts/gamemodes/_gamemode_fastball_intro.gnut index 5f2e6adc..2a137744 100644 --- a/Northstar.Custom/mod/scripts/vscripts/gamemodes/_gamemode_fastball_intro.gnut +++ b/Northstar.Custom/mod/scripts/vscripts/gamemodes/_gamemode_fastball_intro.gnut @@ -107,6 +107,12 @@ void function AnimateBuddy( entity buddy ) buddy.WaitSignal( "fastball_release" ) wait 5.0 + + // clear any players off bt to avoid potential crash which can supposedly happen even though i've never seen it happen + foreach ( entity player in GetPlayerArray() ) + if ( player.GetParent() == buddy ) + player.ClearParent() + buddy.Destroy() } diff --git a/Northstar.Custom/mod/scripts/vscripts/gamemodes/sh_gamemode_fw_custom.nut b/Northstar.Custom/mod/scripts/vscripts/gamemodes/sh_gamemode_fw_custom.nut index 8c6e3f63..ca238d5d 100644 --- a/Northstar.Custom/mod/scripts/vscripts/gamemodes/sh_gamemode_fw_custom.nut +++ b/Northstar.Custom/mod/scripts/vscripts/gamemodes/sh_gamemode_fw_custom.nut @@ -13,8 +13,6 @@ void function SHCreateGamemodeFW_Init() void function CreateGamemodeFW() { - //entity e = CreateEntity("npc_turret_mega"); SetAISettingsWrapper( e, "npc_turret_mega_fortwar" ); e.SetOrigin(GetPlayerArray()[0].GetOrigin()); SetTeam(e,3); DispatchSpawn(e) - // we have to manually add the client/shared scripts to scripts.rson atm so we need to prevent compile errors when they aren't included // best way to do this is to just ignore this whole block for now and wait until we don't have to add them manually diff --git a/Northstar.CustomServers/mod/scripts/vscripts/_menu_callbacks.gnut b/Northstar.CustomServers/mod/scripts/vscripts/_menu_callbacks.gnut index 1feefc2b..02be47a4 100644 --- a/Northstar.CustomServers/mod/scripts/vscripts/_menu_callbacks.gnut +++ b/Northstar.CustomServers/mod/scripts/vscripts/_menu_callbacks.gnut @@ -35,7 +35,8 @@ void function WritePersistenceAndLeaveForLocalPlayerOnly( entity player ) while ( GetPlayerArray().len() != 1 && Time() < time + 5.0 ) WaitFrame() - WritePersistenceAndLeave( player ) + if ( IsValid( player ) ) + WritePersistenceAndLeave( player ) } void function WritePersistenceAndLeave( entity player ) diff --git a/Northstar.CustomServers/mod/scripts/vscripts/burnmeter/_burnmeter.gnut b/Northstar.CustomServers/mod/scripts/vscripts/burnmeter/_burnmeter.gnut index 4be04643..3151a0f2 100644 --- a/Northstar.CustomServers/mod/scripts/vscripts/burnmeter/_burnmeter.gnut +++ b/Northstar.CustomServers/mod/scripts/vscripts/burnmeter/_burnmeter.gnut @@ -49,6 +49,7 @@ void function BurnMeter_Init() AddCallback_OnClientConnected( InitBurncardsForLateJoiner ) AddCallback_OnPlayerRespawned( StartPhaseRewindLifetime ) + AddCallback_OnTitanBecomesPilot( RemoveAmpedWeaponsForTitanPilot ) // necessary signals RegisterSignal( "StopAmpedWeapons" ) @@ -241,7 +242,17 @@ void function PlayerUsesAmpedWeaponsBurncardThreaded( entity player ) { weapon.RemoveMod( "silencer" ) // both this and the burnmod will override firing fx, if a second one overrides this we crash foreach ( string mod in GetWeaponBurnMods( weapon.GetWeaponClassName() ) ) - weapon.AddMod( mod ) + { + // catch incompatibilities just in case + try + { + weapon.AddMod( mod ) + } + catch( ex ) + { + weapons.removebyvalue( weapon ) + } + } // needed to display amped weapon time left weapon.SetScriptFlags0( weapon.GetScriptFlags0() | WEAPONFLAG_AMPED ) @@ -263,6 +274,13 @@ void function PlayerUsesAmpedWeaponsBurncardThreaded( entity player ) } } +void function RemoveAmpedWeaponsForTitanPilot( entity player, entity titan ) +{ + foreach ( entity weapon in player.GetMainWeapons() ) + foreach ( string mod in GetWeaponBurnMods( weapon.GetWeaponClassName() ) ) + weapon.RemoveMod( mod ) +} + void function PlayerUsesSmartPistolBurncard( entity player ) { // take secondary weapon diff --git a/Northstar.CustomServers/mod/scripts/vscripts/evac/_evac.gnut b/Northstar.CustomServers/mod/scripts/vscripts/evac/_evac.gnut index a4f23b85..b861ed9f 100644 --- a/Northstar.CustomServers/mod/scripts/vscripts/evac/_evac.gnut +++ b/Northstar.CustomServers/mod/scripts/vscripts/evac/_evac.gnut @@ -103,11 +103,20 @@ void function EvacEpilogue() if ( canRunEvac ) { - SetRespawnsEnabled( false ) + thread SetRespawnAndWait( false ) thread Evac( GetOtherTeam( winner ), EVAC_INITIAL_WAIT, EVAC_ARRIVAL_TIME, EVAC_WAIT_TIME, EvacEpiloguePlayerCanBoard, EvacEpilogueShouldLeaveEarly, EvacEpilogueCompleted ) } else - thread EvacEpilogueCompleted( null ) // this is hacky but like, this also shouldn't really be hit in normal gameplay + { + thread SetRespawnAndWait( false ) //prevent respawns during the fade to black, should only be an issue if the match is a draw + thread EvacEpilogueCompleted( null ) //this is hacky but like, this also shouldn't really be hit in normal gameplay + } +} + +void function SetRespawnAndWait(bool mode) +{ + wait GAME_EPILOGUE_PLAYER_RESPAWN_LEEWAY + SetRespawnsEnabled( mode ) } bool function EvacEpiloguePlayerCanBoard( entity dropship, entity player ) @@ -386,4 +395,4 @@ void function EvacDropshipKilled( entity dropship, var damageInfo ) player.Die( DamageInfo_GetAttacker( damageInfo ), DamageInfo_GetWeapon( damageInfo ), { damageSourceId = eDamageSourceId.evac_dropship_explosion, scriptType = DF_GIB } ) } } -}
\ No newline at end of file +} diff --git a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_ctf.nut b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_ctf.nut index 7f879c69..dc976059 100644 --- a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_ctf.nut +++ b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_ctf.nut @@ -327,6 +327,7 @@ void function GiveFlag( entity player, entity flag ) void function DropFlagIfPhased( entity player, entity flag ) { player.EndSignal( "StartPhaseShift" ) + player.EndSignal( "OnDestroy" ) OnThreadEnd( function() : ( player ) { diff --git a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_speedball.nut b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_speedball.nut index 49fd5f2e..22c660d8 100644 --- a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_speedball.nut +++ b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_speedball.nut @@ -86,6 +86,7 @@ void function GiveFlag( entity player ) void function DropFlagIfPhased( entity player ) { player.EndSignal( "StartPhaseShift" ) + player.EndSignal( "OnDestroy" ) OnThreadEnd( function() : ( player ) { @@ -102,7 +103,9 @@ void function DropFlag() file.flag.ClearParent() file.flag.SetAngles( < 0, 0, 0 > ) SetGlobalNetEnt( "flagCarrier", file.flag ) - EmitSoundOnEntityOnlyToPlayer( file.flagCarrier, file.flagCarrier, "UI_CTF_1P_FlagDrop" ) + + if ( IsValid( file.flagCarrier ) ) + EmitSoundOnEntityOnlyToPlayer( file.flagCarrier, file.flagCarrier, "UI_CTF_1P_FlagDrop" ) foreach ( entity player in GetPlayerArray() ) MessageToPlayer( player, eEventNotifications.SPEEDBALL_FlagDropped, file.flagCarrier ) diff --git a/Northstar.CustomServers/mod/scripts/vscripts/lobby/_private_lobby.gnut b/Northstar.CustomServers/mod/scripts/vscripts/lobby/_private_lobby.gnut index 08cc9d0b..388f4794 100644 --- a/Northstar.CustomServers/mod/scripts/vscripts/lobby/_private_lobby.gnut +++ b/Northstar.CustomServers/mod/scripts/vscripts/lobby/_private_lobby.gnut @@ -37,6 +37,10 @@ void function SetupPrivateMatchUIVarsWhenReady() bool function ClientCommandCallback_PrivateMatchLaunch( entity player, array<string> args ) { + if ( GetConVarBool( "ns_private_match_only_host_can_start" ) ) + if ( !NSIsPlayerIndexLocalPlayer( player.GetPlayerIndex() ) ) + return true + if ( file.startState == ePrivateMatchStartState.STARTING ) { // cancel start if we're already mid-countdown @@ -46,10 +50,6 @@ bool function ClientCommandCallback_PrivateMatchLaunch( entity player, array<str } else { - if ( GetConVarBool( "ns_private_match_only_host_can_start" ) ) - if ( !NSIsPlayerIndexLocalPlayer( player.GetPlayerIndex() ) ) - return true - // start match file.startState = ePrivateMatchStartState.STARTING thread StartMatch() diff --git a/Northstar.CustomServers/mod/scripts/vscripts/mp/_classic_mp_dropship_intro.gnut b/Northstar.CustomServers/mod/scripts/vscripts/mp/_classic_mp_dropship_intro.gnut index 4f6a1291..d3b4cd7e 100644 --- a/Northstar.CustomServers/mod/scripts/vscripts/mp/_classic_mp_dropship_intro.gnut +++ b/Northstar.CustomServers/mod/scripts/vscripts/mp/_classic_mp_dropship_intro.gnut @@ -64,25 +64,40 @@ void function OnPrematchStart() // make 2 empty dropship structs per team IntroDropship emptyDropship - file.militiaDropships = [ clone emptyDropship, clone emptyDropship ] - file.imcDropships = [ clone emptyDropship, clone emptyDropship ] + file.militiaDropships.clear() + file.imcDropships.clear() - // spawn dropships - foreach ( entity dropshipSpawn in GetEntArrayByClass_Expensive( "info_spawnpoint_dropship_start" ) ) - { + array<entity> validDropshipSpawns + array<entity> dropshipSpawns = GetEntArrayByClass_Expensive( "info_spawnpoint_dropship_start" ) + foreach ( entity dropshipSpawn in dropshipSpawns ) + { if ( dropshipSpawn.HasKey( "gamemode_" + GetSpawnpointGamemodeOverride() ) ) if ( dropshipSpawn.kv[ "gamemode_" + GetSpawnpointGamemodeOverride() ] == "0" ) continue + + validDropshipSpawns.append( dropshipSpawn ) + } + + // if no dropship spawns for this mode, just allow any dropship spawns + if ( validDropshipSpawns.len() < 2 ) + validDropshipSpawns = dropshipSpawns + // spawn dropships + foreach ( entity dropshipSpawn in validDropshipSpawns ) + { // todo: possibly make this only spawn dropships if we've got enough players to need them int createTeam = HasSwitchedSides() ? dropshipSpawn.GetTeam() : GetOtherTeam( dropshipSpawn.GetTeam() ) array<IntroDropship> teamDropships = createTeam == TEAM_MILITIA ? file.militiaDropships : file.imcDropships - int dropshipIndex = !IsValid( teamDropships[ 0 ].dropship ) ? 0 : 1 + if ( teamDropships.len() >= 2 ) + continue + // create entity entity dropship = CreateDropship( createTeam, dropshipSpawn.GetOrigin(), dropshipSpawn.GetAngles() ) - teamDropships[ dropshipIndex ].dropship = dropship + teamDropships.append( clone emptyDropship ) + teamDropships[ teamDropships.len() - 1 ].dropship = dropship + AddAnimEvent( dropship, "dropship_warpout", WarpoutEffect ) dropship.SetValueForModelKey( $"models/vehicle/crow_dropship/crow_dropship_hero.mdl" ) dropship.SetModel( $"models/vehicle/crow_dropship/crow_dropship_hero.mdl" ) @@ -110,6 +125,8 @@ void function EndIntroWhenFinished() void function SpawnPlayerIntoDropship( entity player ) { + player.EndSignal( "OnDestroy" ) + if ( IsAlive( player ) ) player.Die() // kill them so we don't have any issues respawning them later @@ -133,7 +150,6 @@ void function SpawnPlayerIntoDropship( entity player ) WaitFrame() player.EndSignal( "OnDeath" ) - player.EndSignal( "OnDestroy" ) // find the player's dropship and seat array<IntroDropship> teamDropships diff --git a/Northstar.CustomServers/mod/scripts/vscripts/mp/_gamestate_mp.nut b/Northstar.CustomServers/mod/scripts/vscripts/mp/_gamestate_mp.nut index 2f16379e..63ecbf68 100644 --- a/Northstar.CustomServers/mod/scripts/vscripts/mp/_gamestate_mp.nut +++ b/Northstar.CustomServers/mod/scripts/vscripts/mp/_gamestate_mp.nut @@ -183,6 +183,10 @@ void function GameStateEnter_Prematch() void function StartGameWithoutClassicMP() { + foreach ( entity player in GetPlayerArray() ) + if ( IsAlive( player ) ) + player.Die() + WaitFrame() // wait for callbacks to finish // need these otherwise game will complain diff --git a/Northstar.CustomServers/mod/scripts/vscripts/mp/_titan_transfer.nut b/Northstar.CustomServers/mod/scripts/vscripts/mp/_titan_transfer.nut index 7b126cd0..c84e6aba 100644 --- a/Northstar.CustomServers/mod/scripts/vscripts/mp/_titan_transfer.nut +++ b/Northstar.CustomServers/mod/scripts/vscripts/mp/_titan_transfer.nut @@ -178,7 +178,12 @@ void function GiveWeaponsFromStoredArray( entity player, array<StoredWeapon> sto UpdateProScreen( player, weapon ) } - string weaponCategory = GetWeaponInfoFileKeyField_GlobalString( weapon.GetWeaponClassName(), "menu_category" ) + string weaponCategory = "" + if ( IsWeaponKeyFieldDefined(weapon.GetWeaponClassName(), "menu_category") ) + { + weaponCategory = GetWeaponInfoFileKeyField_GlobalString( weapon.GetWeaponClassName(), "menu_category" ) + } + if ( weaponCategory == "at" || weaponCategory == "special" ) // refill AT/grenadier ammo stockpile { int defaultTotal = weapon.GetWeaponSettingInt( eWeaponVar.ammo_default_total ) diff --git a/Northstar.CustomServers/mod/scripts/vscripts/sh_loadouts.nut b/Northstar.CustomServers/mod/scripts/vscripts/sh_loadouts.nut new file mode 100644 index 00000000..d15220e4 --- /dev/null +++ b/Northstar.CustomServers/mod/scripts/vscripts/sh_loadouts.nut @@ -0,0 +1,4061 @@ +#if SERVER
+untyped
+#endif
+
+globalize_all_functions
+
+global string INVALID_REF = "INVALID_REF"
+
+void function InitDefaultLoadouts()
+{
+ PopulateDefaultPilotLoadouts( shGlobal.defaultPilotLoadouts )
+ PopulateDefaultTitanLoadouts( shGlobal.defaultTitanLoadouts )
+}
+
+PilotLoadoutDef function GetDefaultPilotLoadout( int index )
+{
+ return shGlobal.defaultPilotLoadouts[ index ]
+}
+
+TitanLoadoutDef function GetDefaultTitanLoadout( int index )
+{
+ return shGlobal.defaultTitanLoadouts[ index ]
+}
+
+PilotLoadoutDef[NUM_PERSISTENT_PILOT_LOADOUTS] function GetDefaultPilotLoadouts()
+{
+ return shGlobal.defaultPilotLoadouts
+}
+
+TitanLoadoutDef[NUM_PERSISTENT_TITAN_LOADOUTS] function GetDefaultTitanLoadouts()
+{
+ return shGlobal.defaultTitanLoadouts
+}
+
+void function PopulateDefaultPilotLoadouts( PilotLoadoutDef[ NUM_PERSISTENT_PILOT_LOADOUTS ] loadouts )
+{
+ var dataTable = GetDataTable( $"datatable/default_pilot_loadouts.rpak" )
+
+ for ( int i = 0; i < NUM_PERSISTENT_PILOT_LOADOUTS; i++ )
+ {
+ PilotLoadoutDef loadout = loadouts[i]
+ loadout.name = GetDataTableString( dataTable, i, GetDataTableColumnByName( dataTable, "name" ) )
+ loadout.suit = GetDataTableString( dataTable, i, GetDataTableColumnByName( dataTable, "suit" ) )
+ loadout.race = GetDataTableString( dataTable, i, GetDataTableColumnByName( dataTable, "race" ) )
+ loadout.primary = GetDataTableString( dataTable, i, GetDataTableColumnByName( dataTable, "primary" ) )
+ loadout.primaryAttachment = GetDataTableString( dataTable, i, GetDataTableColumnByName( dataTable, "primaryAttachment" ) )
+ loadout.primaryMod1 = GetDataTableString( dataTable, i, GetDataTableColumnByName( dataTable, "primaryMod1" ) )
+ loadout.primaryMod2 = GetDataTableString( dataTable, i, GetDataTableColumnByName( dataTable, "primaryMod2" ) )
+ loadout.primaryMod3 = GetDataTableString( dataTable, i, GetDataTableColumnByName( dataTable, "primaryMod3" ) )
+ loadout.secondary = GetDataTableString( dataTable, i, GetDataTableColumnByName( dataTable, "secondary" ) )
+ loadout.secondaryMod1 = GetDataTableString( dataTable, i, GetDataTableColumnByName( dataTable, "secondaryMod1" ) )
+ loadout.secondaryMod2 = GetDataTableString( dataTable, i, GetDataTableColumnByName( dataTable, "secondaryMod2" ) )
+ loadout.secondaryMod3 = GetDataTableString( dataTable, i, GetDataTableColumnByName( dataTable, "secondaryMod3" ) )
+ loadout.weapon3 = GetDataTableString( dataTable, i, GetDataTableColumnByName( dataTable, "weapon3" ) )
+ loadout.weapon3Mod1 = GetDataTableString( dataTable, i, GetDataTableColumnByName( dataTable, "weapon3Mod1" ) )
+ loadout.weapon3Mod2 = GetDataTableString( dataTable, i, GetDataTableColumnByName( dataTable, "weapon3Mod2" ) )
+ loadout.weapon3Mod3 = GetDataTableString( dataTable, i, GetDataTableColumnByName( dataTable, "weapon3Mod3" ) )
+ loadout.ordnance = GetDataTableString( dataTable, i, GetDataTableColumnByName( dataTable, "ordnance" ) )
+ loadout.passive1 = GetDataTableString( dataTable, i, GetDataTableColumnByName( dataTable, "passive1" ) )
+ loadout.passive2 = GetDataTableString( dataTable, i, GetDataTableColumnByName( dataTable, "passive2" ) )
+
+ //TODO: Why isn't execution initialized here?
+
+ UpdateDerivedPilotLoadoutData( loadout, false )
+
+ //loadout.race
+ //ValidateDefaultLoadoutData( "pilot", loadout.primary )
+ //ValidateWeaponSubitem( loadout.primary, loadout.primaryAttachment, eItemTypes.PILOT_PRIMARY_ATTACHMENT )
+ //ValidateWeaponSubitem( loadout.primary, loadout.primaryMod1, eItemTypes.PILOT_PRIMARY_MOD )
+ //ValidateWeaponSubitem( loadout.primary, loadout.primaryMod2, eItemTypes.PILOT_PRIMARY_MOD )
+ Assert( ( loadout.primaryMod1 == "" && loadout.primaryMod2 == "" ) || ( loadout.primaryMod1 != loadout.primaryMod2 ), "!!! Primary mod1 and mod2 in default pilot loadout: " + loadout.name + " should be different but are both set to: " + loadout.primaryMod1 )
+
+ //ValidateDefaultLoadoutData( "pilot", loadout.secondary )
+ //ValidateWeaponSubitem( loadout.secondary, loadout.secondaryMod1, eItemTypes.PILOT_SECONDARY_MOD )
+ //ValidateWeaponSubitem( loadout.secondary, loadout.secondaryMod2, eItemTypes.PILOT_SECONDARY_MOD )
+ Assert( ( loadout.secondaryMod1 == "" && loadout.secondaryMod2 == "" ) || ( loadout.secondaryMod1 != loadout.secondaryMod2 ), "!!! Secondary mod1 and mod2 in default pilot loadout: " + loadout.name + " should be different but are both set to: " + loadout.secondaryMod1 )
+
+ //ValidateDefaultLoadoutData( "pilot", loadout.weapon3 )
+ //ValidateWeaponSubitem( loadout.weapon3, loadout.weapon3Mod1, eItemTypes.PILOT_SECONDARY_MOD )
+ //ValidateWeaponSubitem( loadout.weapon3, loadout.weapon3Mod2, eItemTypes.PILOT_SECONDARY_MOD )
+ Assert( ( loadout.weapon3Mod1 == "" && loadout.weapon3Mod2 == "" ) || ( loadout.weapon3Mod1 != loadout.weapon3Mod2 ), "!!! Weapon3 mod1 and mod2 in default pilot loadout: " + loadout.name + " should be different but are both set to: " + loadout.weapon3Mod1 )
+
+ //ValidateDefaultLoadoutData( "pilot", loadout.ordnance )
+ //ValidateDefaultLoadoutData( "pilot", loadout.special )
+ //ValidateDefaultLoadoutData( "pilot", loadout.passive1 )
+ //ValidateDefaultLoadoutData( "pilot", loadout.passive2 )
+ }
+}
+
+void function PopulateDefaultTitanLoadouts( TitanLoadoutDef[ NUM_PERSISTENT_TITAN_LOADOUTS ] loadouts )
+{
+ var dataTable = GetDataTable( $"datatable/default_titan_loadouts.rpak" )
+
+ for ( int i = 0; i < NUM_PERSISTENT_TITAN_LOADOUTS; i++ )
+ {
+ TitanLoadoutDef loadout = loadouts[i]
+ loadout.name = GetDataTableString( dataTable, i, GetDataTableColumnByName( dataTable, "name" ) )
+ loadout.titanClass = GetDataTableString( dataTable, i, GetDataTableColumnByName( dataTable, "titanRef" ) )
+ loadout.setFile = GetDataTableString( dataTable, i, GetDataTableColumnByName( dataTable, "setFile" ) )
+ loadout.primaryMod = GetDataTableString( dataTable, i, GetDataTableColumnByName( dataTable, "primaryMod" ) )
+ loadout.special = GetDataTableString( dataTable, i, GetDataTableColumnByName( dataTable, "special" ) )
+ loadout.antirodeo = GetDataTableString( dataTable, i, GetDataTableColumnByName( dataTable, "antirodeo" ) )
+ loadout.passive1 = GetDataTableString( dataTable, i, GetDataTableColumnByName( dataTable, "passive1" ) )
+ loadout.passive2 = GetDataTableString( dataTable, i, GetDataTableColumnByName( dataTable, "passive2" ) )
+ loadout.passive3 = GetDataTableString( dataTable, i, GetDataTableColumnByName( dataTable, "passive3" ) )
+ loadout.passive4 = GetDataTableString( dataTable, i, GetDataTableColumnByName( dataTable, "passive4" ) )
+ loadout.passive5 = GetDataTableString( dataTable, i, GetDataTableColumnByName( dataTable, "passive5" ) )
+ loadout.passive6 = GetDataTableString( dataTable, i, GetDataTableColumnByName( dataTable, "passive6" ) )
+ loadout.titanExecution = GetLoadoutPropertyDefault( "titan", i, "titanExecution" )
+
+ OverwriteLoadoutWithDefaultsForSetFile_ExceptSpecialAndAntiRodeo( loadout )
+
+ //ValidateDefaultLoadoutData( "titan", loadout.setFile )
+ ////loadout.primaryMod
+ //ValidateDefaultLoadoutData( "titan", loadout.special )
+ //ValidateDefaultLoadoutData( "titan", loadout.antirodeo )
+ //ValidateDefaultLoadoutData( "titan", loadout.passive1 )
+ //ValidateDefaultLoadoutData( "titan", loadout.passive2 )
+ }
+}
+
+void function ValidateDefaultLoadoutData( string loadoutType, string itemRef )
+{
+ if ( itemRef == "" )
+ return
+
+ Assert( loadoutType == "pilot" || loadoutType == "titan" )
+
+ string tableFile = "default_" + loadoutType + "_loadouts.csv"
+
+ Assert( ItemDefined( itemRef ), "Datatable \"" + tableFile + "\" contains an unknown item reference: " + itemRef )
+ Assert( GetUnlockLevelReq( itemRef ) <= 1, "Datatable \"" + tableFile + "\" item: " + itemRef + " must be unlocked at level 1" )
+}
+
+void function ValidateWeaponSubitem( string weaponRef, string itemRef, int itemType )
+{
+ bool isPlayerLevelLocked = IsItemLockedForPlayerLevel( 1, weaponRef )
+ bool isWeaponLevelLocked = IsItemLockedForWeaponLevel( 1, weaponRef, itemRef )
+
+ if ( isPlayerLevelLocked || isWeaponLevelLocked )
+ {
+ //array<ItemData> subitems = GetAllSubitemsOfType( weaponRef, itemType )
+ //foreach ( subitem in subitems )
+ //{
+ // if ( !IsItemLockedForWeaponLevel( 1, weaponRef, subitem.ref ) )
+ // printt( " ", subitem.ref )
+ //}
+
+ Assert( 0, "Subitem: " + itemRef + " for item: " + weaponRef + " should either be available by default, or changed to one of the values listed above." )
+ CodeWarning( "Subitem: " + itemRef + " for item: " + weaponRef + " should either be available by default, or changed to one of the values listed above." )
+ }
+}
+
+PilotLoadoutDef function GetPilotLoadoutFromPersistentData( entity player, int loadoutIndex )
+{
+ PilotLoadoutDef loadout
+ PopulatePilotLoadoutFromPersistentData( player, loadout, loadoutIndex )
+
+ return loadout
+}
+
+TitanLoadoutDef function GetTitanLoadoutFromPersistentData( entity player, int loadoutIndex )
+{
+ TitanLoadoutDef loadout
+ PopulateTitanLoadoutFromPersistentData( player, loadout, loadoutIndex )
+
+ return loadout
+}
+
+void function PopulatePilotLoadoutFromPersistentData( entity player, PilotLoadoutDef loadout, int loadoutIndex )
+{
+ loadout.name = GetValidatedPersistentLoadoutValue( player, "pilot", loadoutIndex, "name" )
+ loadout.suit = GetValidatedPersistentLoadoutValue( player, "pilot", loadoutIndex, "suit" )
+ loadout.race = GetValidatedPersistentLoadoutValue( player, "pilot", loadoutIndex, "race" )
+ loadout.execution = GetValidatedPersistentLoadoutValue( player, "pilot", loadoutIndex, "execution" )
+ loadout.primary = GetValidatedPersistentLoadoutValue( player, "pilot", loadoutIndex, "primary" )
+ loadout.primaryAttachment = GetValidatedPersistentLoadoutValue( player, "pilot", loadoutIndex, "primaryAttachment" )
+ loadout.primaryMod1 = GetValidatedPersistentLoadoutValue( player, "pilot", loadoutIndex, "primaryMod1" )
+ loadout.primaryMod2 = GetValidatedPersistentLoadoutValue( player, "pilot", loadoutIndex, "primaryMod2" )
+ loadout.primaryMod3 = GetValidatedPersistentLoadoutValue( player, "pilot", loadoutIndex, "primaryMod3" )
+ loadout.secondary = GetValidatedPersistentLoadoutValue( player, "pilot", loadoutIndex, "secondary" )
+ loadout.secondaryMod1 = GetValidatedPersistentLoadoutValue( player, "pilot", loadoutIndex, "secondaryMod1" )
+ loadout.secondaryMod2 = GetValidatedPersistentLoadoutValue( player, "pilot", loadoutIndex, "secondaryMod2" )
+ loadout.secondaryMod3 = GetValidatedPersistentLoadoutValue( player, "pilot", loadoutIndex, "secondaryMod3" )
+ loadout.weapon3 = GetValidatedPersistentLoadoutValue( player, "pilot", loadoutIndex, "weapon3" )
+ loadout.weapon3Mod1 = GetValidatedPersistentLoadoutValue( player, "pilot", loadoutIndex, "weapon3Mod1" )
+ loadout.weapon3Mod2 = GetValidatedPersistentLoadoutValue( player, "pilot", loadoutIndex, "weapon3Mod2" )
+ loadout.weapon3Mod3 = GetValidatedPersistentLoadoutValue( player, "pilot", loadoutIndex, "weapon3Mod3" )
+ loadout.ordnance = GetValidatedPersistentLoadoutValue( player, "pilot", loadoutIndex, "ordnance" )
+ loadout.passive1 = GetValidatedPersistentLoadoutValue( player, "pilot", loadoutIndex, "passive1" )
+ loadout.passive2 = GetValidatedPersistentLoadoutValue( player, "pilot", loadoutIndex, "passive2" )
+ loadout.camoIndex = GetValidatedPersistentLoadoutValueInt( player, "pilot", loadoutIndex, "camoIndex" )
+ loadout.skinIndex = GetValidatedPersistentLoadoutValueInt( player, "pilot", loadoutIndex, "skinIndex" ) //Important: Skin index needs to be gotten after camoIndex for loadout validation purposes
+ loadout.primaryCamoIndex = GetValidatedPersistentLoadoutValueInt( player, "pilot", loadoutIndex, "primaryCamoIndex" )
+ loadout.primarySkinIndex = GetValidatedPersistentLoadoutValueInt( player, "pilot", loadoutIndex, "primarySkinIndex" ) //Important: Skin index needs to be gotten after camoIndex for loadout validation purposes
+ loadout.secondaryCamoIndex = GetValidatedPersistentLoadoutValueInt( player, "pilot", loadoutIndex, "secondaryCamoIndex" )
+ loadout.secondarySkinIndex = GetValidatedPersistentLoadoutValueInt( player, "pilot", loadoutIndex, "secondarySkinIndex" ) //Important: Skin index needs to be gotten after camoIndex for loadout validation purposes
+ loadout.weapon3CamoIndex = GetValidatedPersistentLoadoutValueInt( player, "pilot", loadoutIndex, "weapon3CamoIndex" )
+ loadout.weapon3SkinIndex = GetValidatedPersistentLoadoutValueInt( player, "pilot", loadoutIndex, "weapon3SkinIndex" ) //Important: Skin index needs to be gotten after camoIndex for loadout validation purposes
+
+ UpdateDerivedPilotLoadoutData( loadout )
+}
+
+void function PopulateTitanLoadoutFromPersistentData( entity player, TitanLoadoutDef loadout, int loadoutIndex )
+{
+ loadout.name = GetValidatedPersistentLoadoutValue( player, "titan", loadoutIndex, "name" )
+ loadout.titanClass = GetValidatedPersistentLoadoutValue( player, "titan", loadoutIndex, "titanClass" )
+ loadout.primaryMod = GetValidatedPersistentLoadoutValue( player, "titan", loadoutIndex, "primaryMod" )
+ loadout.special = GetValidatedPersistentLoadoutValue( player, "titan", loadoutIndex, "special" )
+ loadout.antirodeo = GetValidatedPersistentLoadoutValue( player, "titan", loadoutIndex, "antirodeo" )
+ loadout.passive1 = GetValidatedPersistentLoadoutValue( player, "titan", loadoutIndex, "passive1" )
+ loadout.passive2 = GetValidatedPersistentLoadoutValue( player, "titan", loadoutIndex, "passive2" )
+ loadout.passive3 = GetValidatedPersistentLoadoutValue( player, "titan", loadoutIndex, "passive3" )
+ loadout.passive4 = GetValidatedPersistentLoadoutValue( player, "titan", loadoutIndex, "passive4" )
+ loadout.passive5 = GetValidatedPersistentLoadoutValue( player, "titan", loadoutIndex, "passive5" )
+ loadout.passive6 = GetValidatedPersistentLoadoutValue( player, "titan", loadoutIndex, "passive6" )
+ loadout.camoIndex = GetValidatedPersistentLoadoutValueInt( player, "titan", loadoutIndex, "camoIndex" )
+ loadout.skinIndex = GetValidatedPersistentLoadoutValueInt( player, "titan", loadoutIndex, "skinIndex" ) //Important: Skin index needs to be gotten after camoIndex for loadout validation purposes
+ loadout.decalIndex = GetValidatedPersistentLoadoutValueInt( player, "titan", loadoutIndex, "decalIndex" )
+ loadout.primaryCamoIndex = GetValidatedPersistentLoadoutValueInt( player, "titan", loadoutIndex, "primaryCamoIndex" )
+ loadout.primarySkinIndex = GetValidatedPersistentLoadoutValueInt( player, "titan", loadoutIndex, "primarySkinIndex" ) //Important: Skin index needs to be gotten after camoIndex for loadout validation purposes
+ loadout.titanExecution = GetValidatedPersistentLoadoutValue( player, "titan", loadoutIndex, "titanExecution" )
+ loadout.showArmBadge = GetValidatedPersistentLoadoutValueInt( player, "titan", loadoutIndex, "showArmBadge" )
+
+ //Prime Titan related vars
+ loadout.isPrime = GetValidatedPersistentLoadoutValue( player, "titan", loadoutIndex, "isPrime" )
+ loadout.primeCamoIndex = GetValidatedPersistentLoadoutValueInt( player, "titan", loadoutIndex, "primeCamoIndex" )
+ loadout.primeSkinIndex = GetValidatedPersistentLoadoutValueInt( player, "titan", loadoutIndex, "primeSkinIndex" ) //Important: Skin index needs to be gotten after camoIndex for loadout validation purposes
+ loadout.primeDecalIndex = GetValidatedPersistentLoadoutValueInt( player, "titan", loadoutIndex, "primeDecalIndex" )
+
+ UpdateDerivedTitanLoadoutData( loadout )
+ OverwriteLoadoutWithDefaultsForSetFile( loadout )
+}
+
+string function GetSetFileForTitanClassAndPrimeStatus( string titanClass, bool isPrimeTitan = false )
+{
+ string nonPrimeSetFile
+
+ array<TitanLoadoutDef> legalLoadouts = GetAllowedTitanLoadouts()
+
+ foreach ( loadout in legalLoadouts )
+ {
+ if ( GetTitanCharacterNameFromSetFile( loadout.setFile ) == titanClass )
+ {
+ nonPrimeSetFile = loadout.setFile
+ break
+ }
+ }
+
+ if ( !isPrimeTitan )
+ return nonPrimeSetFile
+
+ string primeSetFile = GetPrimeTitanSetFileFromNonPrimeSetFile( nonPrimeSetFile )
+ Assert( primeSetFile != "" )
+ return primeSetFile
+}
+
+
+string function GetPrimeTitanRefForTitanClass( string titanClass )
+{
+ array<TitanLoadoutDef> legalLoadouts = GetAllowedTitanLoadouts()
+
+ foreach ( loadout in legalLoadouts )
+ {
+ if ( GetTitanCharacterNameFromSetFile( loadout.setFile ) == titanClass )
+ return loadout.primeTitanRef
+ }
+
+ unreachable
+}
+
+string function GetPersistentLoadoutValue( entity player, string loadoutType, int loadoutIndex, string loadoutProperty )
+{
+ // printt( "=======================================================================================" )
+ // printt( "loadoutType:", loadoutType, "loadoutIndex:", loadoutIndex, "loadoutProperty:" , loadoutProperty )
+ // printl( "script GetPlayerArray()[0].SetPersistentVar( \"" + loadoutType + "Loadouts[" + loadoutIndex + "]." + loadoutProperty + "\", \"" + value + "\" )" )
+ // printt( "=======================================================================================" )
+
+ string getString = BuildPersistentVarAccessorString( loadoutType, loadoutIndex, loadoutProperty )
+ var value = player.GetPersistentVar( getString )
+
+ if ( value == null )
+ return ""
+
+ return string( value )
+}
+
+
+int function GetPersistentLoadoutValueInt( entity player, string loadoutType, int loadoutIndex, string loadoutProperty )
+{
+ return int( GetPersistentLoadoutValue( player, loadoutType, loadoutIndex, loadoutProperty ) )
+}
+
+string function GetPersistentLoadoutPropertyType( string loadoutProperty )
+{
+ switch ( loadoutProperty )
+ {
+ case "skinIndex":
+ case "camoIndex":
+ case "decalIndex":
+ case "primeSkinIndex":
+ case "primeCamoIndex":
+ case "primeDecalIndex":
+ case "primarySkinIndex":
+ case "primaryCamoIndex":
+ case "secondarySkinIndex":
+ case "secondaryCamoIndex":
+ case "weapon3SkinIndex":
+ case "weapon3CamoIndex":
+ case "showArmBadge":
+ return "int"
+ }
+
+ return "string"
+}
+
+
+#if SERVER
+// TODO: If we change a property that has a parent or child relationship, all related properties need updating if invalid
+// A parent change should validate children and set invalid to defaults
+// If a child change is invalid for the parent, it should be changed to a valid default based on the parent
+// TODO: Return type. Currently getting passed to AddClientCommandCallback() which expects a bool return, but this isn't setup to return values.
+function SetPersistentLoadoutValue( entity player, string loadoutType, int loadoutIndex, string loadoutProperty, string value )
+{
+ //printt( "=======================================================================================" )
+ //printt( "SetPersistentLoadoutValue called with loadoutType:", loadoutType, "loadoutIndex:", loadoutIndex, "loadoutProperty:" , loadoutProperty, "value:", value )
+ //printl( "script GetPlayerArray()[0].SetPersistentVar( \"" + loadoutType + "Loadouts[" + loadoutIndex + "]." + loadoutProperty + "\", \"" + value + "\" )" )
+ //printt( "=======================================================================================" )
+
+ bool loadoutIsPilot = ( loadoutType == "pilot" )
+ bool loadoutIsTitan = ( loadoutType == "titan" )
+ bool loadoutIsPilotOrTitan = ( loadoutIsPilot || loadoutIsTitan )
+
+ //Assert( loadoutIsPilotOrTitan, "Invalid loadoutType that is not pilot or titan" )
+ if ( !loadoutIsPilotOrTitan )
+ {
+ CodeWarning( "Loadout neither pilot or titan" )
+ return
+ }
+
+ //Assert( ( loadoutIsPilot && IsValidPilotLoadoutIndex( loadoutIndex ) ) || ( loadoutIsTitan && !IsValidTitanLoadoutIndex( loadoutIndex ) ), "SetPersistentLoadoutValue() called with invalid loadoutIndex: " + loadoutIndex )
+
+ if ( loadoutIsPilot && !IsValidPilotLoadoutIndex( loadoutIndex ) )
+ {
+ CodeWarning( "!IsValidPilotLoadoutIndex" )
+ return
+ }
+
+ if ( loadoutIsTitan && !IsValidTitanLoadoutIndex( loadoutIndex ) )
+ {
+ CodeWarning( "!IsValidTitanLoadoutIndex" )
+ return
+ }
+
+ if ( value == "none" )
+ value = ""
+
+ var loadoutPropertyEnum = null
+ if ( loadoutIsPilot )
+ {
+ bool isValid = IsValidPilotLoadoutProperty( loadoutProperty )
+ //Assert( IsValidPilotLoadoutProperty, "SetPersistentLoadoutValue() called with invalid pilot loadoutProperty: " + loadoutProperty )
+ if ( !isValid )
+ {
+ CodeWarning( "!IsValidPilotLoadoutProperty" )
+ return
+ }
+
+ loadoutPropertyEnum = GetPilotLoadoutPropertyEnum( loadoutProperty )
+ }
+ else
+ {
+ bool isValid = IsValidTitanLoadoutProperty( loadoutProperty )
+ //Assert( isValidTitanLoadoutProperty, "SetPersistentLoadoutValue() called with invalid titan loadoutProperty: " + loadoutProperty )
+ if ( !isValid )
+ {
+ CodeWarning( "!IsValidTitanLoadoutProperty" )
+ return
+ }
+
+ loadoutPropertyEnum = GetTitanLoadoutPropertyEnum( loadoutProperty )
+ }
+
+ if ( loadoutPropertyEnum != null && !PersistenceEnumValueIsValid( loadoutPropertyEnum, value ) )
+ {
+ CodeWarning( loadoutType + " " + loadoutProperty + " value: " + value + " not valid in persistent data!" )
+ return
+ }
+
+ if ( LoadoutPropertyRequiresItemValidation( loadoutProperty ) && value != "" )
+ {
+ if ( FailsLoadoutValidationCheck( player, loadoutType, loadoutIndex, loadoutProperty, value ) )
+ {
+ printt( "=======================================================================================" )
+ printt( "SetPersistentLoadoutValue failed FailsLoadoutValidationCheck with loadoutType:", loadoutType, "loadoutIndex:", loadoutIndex, "loadoutProperty:" , loadoutProperty, "value:", value )
+ printt( "=======================================================================================" )
+
+ //Assert( false, "SetPersistentLoadoutValue() call " + loadoutType + " " + loadoutProperty + ", value: " + value + " failed FailsLoadoutValidationCheck() check!" )
+ return
+ }
+ }
+
+ // Only checks primary mods and attachments are valid in itemData and the parent isn't locked
+ if ( !IsLoadoutSubitemValid( player, loadoutType, loadoutIndex, loadoutProperty, value ) )
+ {
+ CodeWarning( loadoutType + " " + loadoutProperty + " value: " + value + " failed IsLoadoutSubitemValid() check! It may not exist in itemData, or it's parent item is locked." )
+ return
+ }
+
+ SetPlayerPersistentVarWithoutValidation( player, loadoutType, loadoutIndex, loadoutProperty, value )
+
+ // Reset child properties when parent changes
+ ResolveInvalidLoadoutChildValues( player, loadoutType, loadoutIndex, loadoutProperty, value )
+ ValidateSkinAndCamoIndexesAsAPair( player, loadoutType, loadoutIndex, loadoutProperty, value ) //Skin and camo properties need to be set correctly as a pair
+
+ #if HAS_THREAT_SCOPE_SLOT_LOCK
+ if ( loadoutProperty.tolower() == "primaryattachment" && value == "threat_scope" )
+ {
+ SetPlayerPersistentVarWithoutValidation( player, loadoutType, loadoutIndex, "primaryMod2", "" )
+ }
+ #endif
+
+ // TEMP client model update method
+ bool updateModel = ( loadoutProperty == "suit" || loadoutProperty == "race" || loadoutProperty == "setFile" || loadoutProperty == "primary" || loadoutProperty == "isPrime" )
+
+ if ( loadoutType == "pilot" )
+ {
+ player.p.pilotLoadoutChanged = true
+
+ if ( updateModel )
+ player.p.pilotModelNeedsUpdate = loadoutIndex
+ }
+ else
+ {
+ player.p.titanLoadoutChanged = true
+
+ if ( updateModel )
+ player.p.titanModelNeedsUpdate = loadoutIndex
+ }
+
+ thread UpdateCachedLoadouts()
+}
+
+string function GetRefFromLoadoutTypeIndexPropertyAndValue( entity player, string loadoutType, int index, string property, var value )
+{
+ bool isPilotLoadout = ( loadoutType == "pilot" )
+ bool isTitanLoadout = ( loadoutType == "titan" )
+ if ( !isPilotLoadout && !isTitanLoadout )
+ return INVALID_REF
+
+ switch ( property )
+ {
+ case "isPrime":
+ {
+ if ( !isTitanLoadout )
+ return INVALID_REF
+
+ if( PersistenceEnumValueIsValid( "titanIsPrimeTitan", value ) )
+ return GetTitanRefFromPersistenceLoadoutIndexAndValueForIsPrime( index, expect string( value ) )
+ else
+ return INVALID_REF
+ }
+
+ case "camoIndex": //Camos work across all Titans, all pilots, and all weapons.
+ case "primeCamoIndex":
+ {
+ int valueAsInt = ForceCastVarToInt( value )
+ if ( !IsValidCamoIndex( valueAsInt ) ) //Note that we should have already checked for the case that we're setting a Titan's camo to -1 ( valid warpaint use case ) in FailsLoadoutValidationCheck()
+ {
+ return INVALID_REF
+ }
+ else
+ {
+ if ( isPilotLoadout )
+ return CamoSkins_GetByIndex( valueAsInt ).pilotCamoRef
+ else
+ return CamoSkins_GetByIndex( valueAsInt ).titanCamoRef
+ }
+ }
+ break
+
+ case "primaryCamoIndex":
+ case "secondaryCamoIndex":
+ case "weapon3CamoIndex":
+ {
+ int valueAsInt = ForceCastVarToInt( value )
+ if ( !IsValidCamoIndex( valueAsInt ) )
+ return INVALID_REF
+ else
+ return CamoSkins_GetByIndex( valueAsInt ).ref
+ }
+ break
+
+ case "primarySkinIndex":
+ case "secondarySkinIndex":
+ case "weapon3SkinIndex":
+ {
+ string parentProperty = GetParentLoadoutProperty( loadoutType, property )
+ string weaponRef = GetPersistentLoadoutValue( player, loadoutType, index, parentProperty )
+ int valueAsInt = ForceCastVarToInt( value )
+ return GetSkinRefFromWeaponRefAndPersistenceValue( weaponRef, valueAsInt )
+ }
+ break
+
+ case "decalIndex":
+ case "primeDecalIndex": //Assume same nose arts work for both prime and non-prime titans
+ {
+ if ( !isTitanLoadout )
+ return INVALID_REF
+
+ string titanClass = GetDefaultTitanLoadout( index ).titanClass
+ int valueAsInt = ForceCastVarToInt( value )
+ return GetNoseArtRefFromTitanClassAndPersistenceValue( titanClass, valueAsInt )
+ }
+ break
+
+ case "primeSkinIndex": //Primes don't have war paints, white listed skins for titans (0 and 2) should have already been handled earlier
+ {
+ return INVALID_REF
+ }
+ break
+
+ case "skinIndex":
+ {
+ if ( !isTitanLoadout ) //Only titans have warpaints. White listed skins for pilots should already have been handled earlier
+ return INVALID_REF
+
+ string titanClass = GetDefaultTitanLoadout( index ).titanClass
+ int valueAsInt = ForceCastVarToInt( value )
+ return GetSkinRefFromTitanClassAndPersistenceValue( titanClass, valueAsInt )
+ }
+ break
+
+ default:
+ return string( value )
+ }
+
+ unreachable
+}
+
+bool function IsValidCamoIndex( int camoIndex )
+{
+ int numCamos = CamoSkins_GetCount() //Note that we should have already handled the case for a titan's camoIndex to be -1 (valid warpaint usecase) in FailsLoadoutValidationCheck
+ return ((camoIndex >= 0) && (camoIndex < numCamos))
+}
+
+string function GetTitanRefFromPersistenceLoadoutIndexAndValueForIsPrime( int index, string value )
+{
+ bool isPrimeRef = (value == "titan_is_prime")
+ string titanClass = GetDefaultTitanLoadout( index ).titanClass
+ if ( isPrimeRef )
+ return GetPrimeTitanRefForTitanClass( titanClass )
+
+ return titanClass
+}
+
+bool function LoadoutPropertyRequiresItemValidation( string loadoutProperty )
+{
+ bool shouldSkipValidation = ( GetCurrentPlaylistVarInt( "skip_loadout_validation", 0 ) == 1 )
+ if ( shouldSkipValidation )
+ {
+ //printt( "skip_loadout_validation" )
+ return false
+ }
+
+ if ( loadoutProperty == "name" )
+ return false
+
+ if ( loadoutProperty == "showArmBadge" )
+ return false
+
+ if ( loadoutProperty == "primarySkinIndex" || loadoutProperty == "secondarySkinIndex" || loadoutProperty == "weapon3SkinIndex")
+ return false
+
+ return true
+}
+
+// Only checks primary mods and attachments are valid in itemData and the parent isn't locked
+bool function IsLoadoutSubitemValid( entity player, string loadoutType, int loadoutIndex, string property, string ref )
+{
+ string childRef = ""
+
+ switch ( property )
+ {
+ case "primaryAttachment":
+ case "primaryMod1":
+ case "primaryMod2":
+ case "primaryMod3":
+ case "secondaryMod1":
+ case "secondaryMod2":
+ case "secondaryMod3":
+ case "weapon3Mod1":
+ case "weapon3Mod2":
+ case "weapon3Mod3":
+ if ( loadoutType == "pilot" )
+ {
+ string parentProperty = GetParentLoadoutProperty( loadoutType, property )
+ string loadoutString = BuildPersistentVarAccessorString( loadoutType, loadoutIndex, parentProperty )
+ if ( ref != "" )
+ childRef = ref
+ ref = expect string( player.GetPersistentVar( loadoutString ) )
+ }
+ break
+ case "skinIndex":
+ case "camoIndex":
+ case "decalIndex":
+ case "primarySkinIndex":
+ case "primaryCamoIndex":
+ case "secondarySkinIndex":
+ case "secondaryCamoIndex":
+ case "weapon3SkinIndex":
+ case "weapon3CamoIndex":
+ case "primeSkinIndex":
+ case "primeCamoIndex":
+ case "primeDecalIndex":
+ return true
+ break
+ }
+ // TODO: Seems bad to pass null childRef on to some of the checks below if the property wasn't one of the above
+
+ // invalid attachment
+ if ( childRef != "" && !HasSubitem( ref, childRef ) )
+ return false
+
+ //if ( IsItemLocked( player, childRef, expect string( ref ) ) )
+ // return false
+
+ return true
+}
+
+void function SetPlayerPersistentVarWithoutValidation( entity player, string loadoutType, int loadoutIndex, string propertyName, string value )
+{
+ string persistentLoadoutString = BuildPersistentVarAccessorString( loadoutType, loadoutIndex, propertyName )
+
+ if ( GetPersistentLoadoutPropertyType( propertyName ) == "int" )
+ player.SetPersistentVar( persistentLoadoutString, int( value ) )
+ else
+ player.SetPersistentVar( persistentLoadoutString, value )
+}
+
+string function ResetLoadoutPropertyToDefault( entity player, string loadoutType, int loadoutIndex, string propertyName )
+{
+ string defaultValue = GetLoadoutPropertyDefault( loadoutType, loadoutIndex, propertyName )
+ SetPlayerPersistentVarWithoutValidation( player, loadoutType, loadoutIndex, propertyName, defaultValue )
+ return defaultValue
+}
+
+bool function FailsLoadoutValidationCheck( entity player, string loadoutType, int loadoutIndex, string loadoutProperty, var value )
+{
+ //printt( "FailsLoadoutValidationCheck, player: " + player + ", loadoutType: " + loadoutType + ", loadoutIndex: " + loadoutIndex + ", loadoutProperty: " + loadoutProperty + ", value: " + value )
+
+ int initializedVersion = player.GetPersistentVarAsInt( "initializedVersion" )
+ bool previouslyInitialized = ( initializedVersion >= PERSISTENCE_INIT_VERSION )
+ if ( !previouslyInitialized ) //Special case: if we're intializing from defaults/updating persistent data, don't try to reset to defaults
+ {
+ //printt( "!previouslyInitialized" )
+ return false
+ }
+
+ if ( AllowCamoIndexToSkipValidation( loadoutType, loadoutProperty, value ) ) //Special case: Warpaints will set camoIndex
+ {
+// printt( "AllowCamoIndexToSkipValidation" )
+ return false
+ }
+
+ if ( AllowSkinIndexToSkipValidation( loadoutType, loadoutProperty, value ) ) //Skins need to be handled differently because they are only valid refs for warpaints, and we set values of 0, 1 and 2 for different cases
+ {
+// printt( "AllowSkinIndexToSkipValidation" )
+ return false
+ }
+
+ string ref = GetRefFromLoadoutTypeIndexPropertyAndValue( player, loadoutType, loadoutIndex, loadoutProperty, value )
+ if ( !IsRefValid( ref ) )
+ {
+ printt( "!IsRefValid( " + ref + " ), generated from loadoutType: " + loadoutType + ", loadoutIndex: " + loadoutIndex + ". loadoutProperty: " + loadoutProperty + ", value: " + value )
+ return true
+ }
+
+ if ( IsSettingPrimeTitanWithoutSetFile( player, loadoutType, loadoutProperty, loadoutIndex, value ) ) //Hack, working around fact that we allow setting Prime Titan value since it's entitlement based unlock.
+ {
+ //printt( "IsSettingPrimeTitanWithoutSetFile, player: " + player + ", ref: " + ref )
+ return true
+ }
+
+ if ( FailsItemLockedValidationCheck( player, loadoutType, loadoutIndex, loadoutProperty, ref ) )
+ {
+ printt( "FailsItemLockedValidationCheck, player: " + player + ", ref: " + ref )
+ return true
+ }
+
+ if ( !IsValueValidForLoadoutTypeIndexAndProperty( loadoutType, loadoutIndex, loadoutProperty, value, ref ) )
+ {
+ //printt( "!IsValueValidForLoadoutTypeIndexAndProperty, loadoutType: " + loadoutType + ", loadoutIndex: " + loadoutIndex + ", loadoutProperty: " + loadoutProperty + ", value: " + value + " ref: " + ref )
+ return true
+ }
+
+ if ( ( loadoutProperty == "secondary" || loadoutProperty == "weapon3" ) && !IsValueValidForPropertyWithCategoryRestriction( player, loadoutIndex, loadoutProperty, string( value ) ) )
+ return true
+
+ //printt( "End FailsLoadoutValidationCheck" )
+ return false
+}
+
+bool function IsValueValidForPropertyWithCategoryRestriction( entity player, int loadoutIndex, string targetProperty, string targetValue )
+{
+ Assert( targetProperty == "secondary" || targetProperty == "weapon3" )
+
+ string otherProperty
+ if ( targetProperty == "secondary" )
+ otherProperty = "weapon3"
+ else
+ otherProperty = "secondary"
+
+ string otherValue = GetPersistentLoadoutValue( player, "pilot", loadoutIndex, otherProperty )
+
+ if ( ItemsInSameMenuCategory( targetValue, otherValue ) )
+ return false
+
+ return true
+}
+
+bool function FailsItemLockedValidationCheck( entity player, string loadoutType, int loadoutIndex, string loadoutProperty, string ref )
+{
+ bool shouldSkipValidation = ( GetCurrentPlaylistVarInt( "skip_loadout_item_locked_validation", 0 ) == 1 )
+ if ( shouldSkipValidation )
+ {
+ //printt( "skip_loadout_item_locked_validation" )
+ return false
+ }
+
+ if ( LoadoutIsLocked( player, loadoutType, loadoutIndex ) )
+ {
+ //printt( "loadout of type " + loadoutType + " and index " + loadoutIndex + " is locked, skip checking if items under it are locked" )
+ return false
+ }
+
+ if ( IsSubItemType( GetItemType( ref ) ) )
+ {
+ string parentRef = GetParentRefFromLoadoutInfoAndRef( player, loadoutType, loadoutIndex, loadoutProperty, ref )
+ if ( parentRef == "" )
+ {
+ //printt( "parentRef == blankstring" )
+ return true
+ }
+
+ if ( !SkipItemLockedCheck( player, ref, parentRef, loadoutProperty ) )
+ {
+ if ( IsSubItemLocked( player, ref, parentRef ) )
+ {
+ //printt( "IsSubItemLocked, player: " + player + ", ref: " + ref + ", parentRef: " + parentRef )
+ return true
+ }
+ }
+ }
+ else
+ {
+ if ( !SkipItemLockedCheck( player, ref, "", loadoutProperty ) )
+ {
+ if ( IsItemLocked( player, ref ) ) //Somehow we are trying to get something that is locked. Might be rogue client sending bad values?
+ {
+ //printt( "IsItemLocked, player: " + player + ", ref: " + ref )
+ return true
+ }
+ }
+ }
+
+ return false
+}
+
+bool function IsSettingPrimeTitanWithoutSetFile( entity player, string loadoutType, string loadoutProperty, int loadoutIndex, var value ) //Temp function, remove once all titans have prime Titans
+{
+ if ( loadoutType != "titan" )
+ return false
+
+ if ( loadoutProperty != "isPrime" )
+ return false
+
+ if ( value != "titan_is_prime" )
+ return false
+
+ string titanClass = GetPersistentLoadoutValue( player, "titan", loadoutIndex, "titanClass" )
+
+ return ( !TitanClassHasPrimeTitan( titanClass ) )
+}
+
+bool function SkipItemLockedCheck( entity player, string ref, string parentRef, string loadoutProperty ) //Hack: Skip entitlement related unlock checks for now. Can fail.
+{
+ if ( DevEverythingUnlocked() )
+ return true
+
+ //if ( IsItemInEntitlementUnlock( ref ) && IsLobby() ) //TODO: Look into restricting this to lobby only? But entitlement checks can fail randomly...
+ if ( IsItemInEntitlementUnlock( ref, parentRef ) )
+ return true
+
+ if ( loadoutProperty == "isPrime" ) //Temp check to avoid weirdness where we can set titan_is_prime but not titan_is_not_prime because prime titan refs are not child refs of non prime titan refs. Should be able to remove this once all prime titans are in game.
+ return true
+
+ return false
+}
+
+bool function AllowCamoIndexToSkipValidation( string loadoutType, string loadoutProperty, var value ) //HACK: working around the fact that we do SetCamo( -1 ) when applying warpaints
+{
+ if ( loadoutProperty != "camoIndex" ) //Prime Titans currently don't have warpaints, so not supporting setting primeCamoIndex to be -1
+ return false
+
+ int valueAsInt = ForceCastVarToInt( value )
+ if ( valueAsInt == CAMO_INDEX_BASE )
+ return true
+
+ if ( loadoutType != "titan" )
+ return false
+
+ if ( valueAsInt != TITAN_WARPAINT_CAMO_INDEX )
+ return false
+
+ return true
+}
+
+bool function AllowSkinIndexToSkipValidation( string loadoutType, string loadoutProperty, var value ) //TODO: test weapon skin/camo combinations?
+{
+ int valueAsInt = ForceCastVarToInt( value ) //TODO: Awkward, getting around the fact that you can't do int( value ) if it's already an int.
+
+ switch( loadoutProperty )
+ {
+ case "skinIndex":
+ {
+ if ( loadoutType == "pilot" )
+ return ( (valueAsInt == SKIN_INDEX_BASE) || (valueAsInt == PILOT_SKIN_INDEX_CAMO) )
+ else
+ return ( (valueAsInt == SKIN_INDEX_BASE) || (valueAsInt == TITAN_SKIN_INDEX_CAMO) ) //War paints will have different values, hence we must validate them
+ }
+ break
+
+ case "primeSkinIndex": //Prime Titans don't support war paints, so should only support values of 0 and 2
+ {
+ return ( (valueAsInt == SKIN_INDEX_BASE) || (valueAsInt == TITAN_SKIN_INDEX_CAMO) )
+ }
+ break
+
+ case "primarySkinIndex":
+ case "secondarySkinIndex":
+ case "weapon3SkinIndex":
+ {
+ return ( (valueAsInt == SKIN_INDEX_BASE) || (valueAsInt == WEAPON_SKIN_INDEX_CAMO) )
+ }
+ break
+
+ default:
+ return false
+ }
+
+ unreachable
+}
+
+bool function IsValueValidForLoadoutTypeIndexAndProperty( string loadoutType, int loadoutIndex, string loadoutProperty, var value, string ref )
+{
+ bool isTitanLoadout = (loadoutType == "titan")
+ bool isPilotLoadout = (loadoutType == "pilot")
+ if ( !isTitanLoadout && !isPilotLoadout )
+ return false
+
+ //Should have run IsRefValid( ref already, so should be fine just doing GetItemType( ref ) )
+ int itemType = GetItemType( ref )
+
+ //printt( "itemType : " + itemType )
+ switch ( loadoutProperty )
+ {
+ case "suit":
+ return ( (itemType == eItemTypes.PILOT_SUIT) && isPilotLoadout )
+
+ case "race":
+ return ( (itemType == eItemTypes.RACE) && isPilotLoadout )
+
+ case "execution":
+ return ( (itemType == eItemTypes.PILOT_EXECUTION) && isPilotLoadout )
+
+ case "titanExecution":
+ return ( isTitanLoadout && IsValidTitanExecution( loadoutIndex, loadoutProperty, value, ref ) )
+
+ case "primary": //Only Pilots store their primary in persistence
+ return ( (itemType == eItemTypes.PILOT_PRIMARY) && isPilotLoadout )
+
+ case "primaryAttachment": //Only Pilots their primary in persistence
+ return ( (itemType == eItemTypes.SUB_PILOT_WEAPON_ATTACHMENT || itemType == eItemTypes.PILOT_PRIMARY_ATTACHMENT) && isPilotLoadout )
+
+ case "primaryMod1": //Only Pilots have primary mods in persistence
+ case "primaryMod2":
+ case "primaryMod3":
+ return ( (itemType == eItemTypes.SUB_PILOT_WEAPON_MOD || itemType == eItemTypes.PILOT_PRIMARY_MOD) && isPilotLoadout )
+
+ case "secondary": //Only Pilots store their secondary in persistence
+ case "weapon3":
+ return ( (itemType == eItemTypes.PILOT_SECONDARY) && isPilotLoadout )
+
+ case "secondaryMod1": //Only Pilots store their secondary in persistence
+ case "secondaryMod2": //Only Pilots store their secondary in persistence
+ case "secondaryMod3": //Only Pilots store their secondary in persistence
+ case "weapon3Mod1":
+ case "weapon3Mod2":
+ case "weapon3Mod3":
+ return ( (itemType == eItemTypes.SUB_PILOT_WEAPON_MOD || itemType == eItemTypes.PILOT_SECONDARY_MOD) && isPilotLoadout )
+
+ case "ordnance":
+ return ( (itemType == eItemTypes.PILOT_ORDNANCE) && isPilotLoadout )
+
+ case "passive1":
+ if ( isPilotLoadout )
+ {
+ return ( itemType == eItemTypes.PILOT_PASSIVE1 )
+ }
+ else
+ {
+ return IsValidTitanPassive( loadoutIndex, loadoutProperty, value, ref )
+ }
+
+ case "passive2":
+ if ( isPilotLoadout )
+ return ( itemType == eItemTypes.PILOT_PASSIVE2 )
+ else
+ return IsValidTitanPassive( loadoutIndex, loadoutProperty, value, ref )
+
+ case "passive3":
+ case "passive4":
+ case "passive5":
+ case "passive6":
+ return ( isTitanLoadout && IsValidTitanPassive( loadoutIndex, loadoutProperty, value, ref ) )
+
+ case "isPrime":
+ return ( isTitanLoadout && PersistenceEnumValueIsValid( "titanIsPrimeTitan", value ) ) //Technically will be covered by enumValue checks before LoadoutValidation checks, but included here for completeness
+
+ //TODO: Need to get ref for these correctly!
+ case "skinIndex":
+ case "camoIndex":
+ case "primarySkinIndex":
+ case "primaryCamoIndex":
+ case "secondarySkinIndex":
+ case "secondaryCamoIndex":
+ case "weapon3SkinIndex":
+ case "weapon3CamoIndex":
+ case "decalIndex":
+ case "primeSkinIndex":
+ case "primeCamoIndex":
+ case "primeDecalIndex":
+ return true // assume already validated
+
+ case "titanClass": //Should never be able to set these things normally!
+ case "primaryMod":
+ case "special":
+ case "antirodeo":
+ string defaultValue = GetLoadoutPropertyDefault( loadoutType, loadoutIndex, loadoutProperty )
+ return ( value == defaultValue )
+
+ default:
+ Assert( false, "Unknown loadoutProperty " + loadoutProperty )
+ return false
+ }
+
+ unreachable
+}
+
+bool function ValidateSkinAndCamoIndexesAsAPair( entity player, string loadoutType, int loadoutIndex, string loadoutProperty, var value )
+{
+ bool isSkinIndex = false
+ switch( loadoutProperty )
+ {
+ case "skinIndex":
+ case "primeSkinIndex":
+ case "primarySkinIndex":
+ case "secondarySkinIndex":
+ case "weapon3SkinIndex":
+ isSkinIndex = true
+ break
+ }
+
+ if ( !isSkinIndex )
+ return false
+
+ int valueAsInt = ForceCastVarToInt( value )
+
+ string camoProperty = GetCorrectCamoProperty( loadoutProperty )
+ int camoIndexValue = GetValidatedPersistentLoadoutValueInt( player, loadoutType, loadoutIndex, camoProperty )
+
+
+ //printt( "ValidateSkinAndCamoIndexesAsAPair, player: " + player + " loadouType: " + loadoutType + ", loadoutIndex: " + loadoutIndex + ", skinProperty: " + loadoutProperty + ", skin Value: " + string( value ) + ", camoProperty: " + camoProperty + ", camo Value: " + camoIndexValue )
+
+ if ( valueAsInt == SKIN_INDEX_BASE ) //Just set to default of 0, 0.
+ return SetCamoIndexToValue( player, loadoutType, loadoutIndex, camoProperty, CAMO_INDEX_BASE )
+
+ switch ( loadoutProperty )
+ {
+ case "skinIndex":
+ {
+ if (loadoutType == "pilot" ) //1 is skin for camo, only other valid value is 0
+ {
+ if ( valueAsInt == PILOT_SKIN_INDEX_CAMO && camoIndexValue <= CAMO_INDEX_BASE )
+ {
+ ResetSkinAndCamoIndexesToZero( player, loadoutType, loadoutIndex, loadoutProperty, camoProperty )
+ return true
+ }
+ }
+ else if ( loadoutType == "titan" ) //2 is skin for camos, all other skin values other than 0 are warpaints that have camo index == -1
+ {
+ if ( valueAsInt == TITAN_SKIN_INDEX_CAMO ) //This is a camo skin
+ {
+ if ( camoIndexValue <= CAMO_INDEX_BASE )
+ {
+ ResetSkinAndCamoIndexesToZero( player, loadoutType, loadoutIndex, loadoutProperty, camoProperty )
+ return true
+ }
+ }
+ else //i.e. this is a warpaint
+ {
+ return SetCamoIndexToValue( player, loadoutType, loadoutIndex, camoProperty, TITAN_WARPAINT_CAMO_INDEX ) //Set to default of -1 for warpaint
+ }
+ }
+
+ break
+ }
+
+ case "primeSkinIndex": //switching between prime and non-prime doesn't have problems where skin is unlocked on non-prime but is locked on prime, so no need to return false
+ {
+ if ( valueAsInt == TITAN_SKIN_INDEX_CAMO && camoIndexValue <= CAMO_INDEX_BASE )
+ {
+ ResetSkinAndCamoIndexesToZero( player, loadoutType, loadoutIndex, loadoutProperty, camoProperty )
+ return true
+ }
+
+ break
+ }
+ case "primarySkinIndex":
+ case "secondarySkinIndex":
+ case "weapon3SkinIndex": //1 is skin for camo
+ {
+ if ( valueAsInt == WEAPON_SKIN_INDEX_CAMO )
+ {
+ if ( camoIndexValue <= CAMO_INDEX_BASE )
+ {
+ ResetSkinAndCamoIndexesToZero( player, loadoutType, loadoutIndex, loadoutProperty, camoProperty )
+ return false // HACK: Work around fact that when we switch weapons we don't do a corresponding call to set camo and skin indexes
+ }
+ }
+ else
+ {
+ return SetCamoIndexToValue( player, loadoutType, loadoutIndex, camoProperty, 0 ) //Set to default of 0 for weapon skins
+ }
+
+ break
+ }
+ }
+
+ return false
+}
+
+bool function SetCamoIndexToValue( entity player, string loadoutType, int loadoutIndex, string camoProperty, int setValue ) //HACK HACK: should fix dependency between skin and camo index on UI side. Remove when done.
+{
+ int camoIndexValue = GetPersistentLoadoutValueInt( player, loadoutType, loadoutIndex, camoProperty )
+
+ if ( camoIndexValue == setValue )
+ return false
+
+ //printt( "SetCamoIndexToValue, player: " + player + " loadouType: " + loadoutType + ", loadoutIndex: " + loadoutIndex + ", camoProperty: " + camoProperty + ", setValue: " + string( setValue ))
+
+ SetPlayerPersistentVarWithoutValidation( player, loadoutType, loadoutIndex, camoProperty, string( setValue ) )
+
+ return true
+}
+
+void function ResetSkinAndCamoIndexesToZero( entity player, string loadoutType, int loadoutIndex, string skinProperty, string camoProperty )
+{
+ SetPlayerPersistentVarWithoutValidation( player, loadoutType, loadoutIndex, camoProperty, "0" )
+ SetPlayerPersistentVarWithoutValidation( player, loadoutType, loadoutIndex, skinProperty, "0" )
+}
+
+string function GetCorrectCamoProperty( string loadoutProperty ) //HACK HACK: should fix dependency between skin and camo index on UI side. Remove when done.
+{
+ string returnValue
+ switch( loadoutProperty )
+ {
+ case "skinIndex":
+ returnValue = "camoIndex"
+ break
+
+ case "primeSkinIndex":
+ returnValue = "primeCamoIndex"
+ break
+
+ case "primarySkinIndex":
+ returnValue = "primaryCamoIndex"
+ break
+
+ case "secondarySkinIndex":
+ returnValue = "secondaryCamoIndex"
+ break
+
+ case "weapon3SkinIndex":
+ returnValue = "weapon3CamoIndex"
+ break
+
+ default:
+ unreachable
+ }
+
+ return returnValue
+}
+
+bool function IsValidTitanPassive( int loadoutIndex, string loadoutProperty, var value, string ref ) //TODO: Not using all parameters in this function yet, might need them for full validation
+{
+ //Should have run IsRefValid(ref already, so fine to do GetItemType( ref ))
+ int itemType = GetItemType( ref )
+ //printt( "itemType: " + itemType )
+
+ switch ( loadoutProperty )
+ {
+ case "passive1":
+ return (itemType == eItemTypes.TITAN_GENERAL_PASSIVE)
+
+ case "passive2":
+ {
+ switch( loadoutIndex ) //TODO: Hard coded, not great!
+ {
+ case 0:
+ return itemType == eItemTypes.TITAN_ION_PASSIVE
+
+ case 1:
+ return itemType == eItemTypes.TITAN_SCORCH_PASSIVE
+
+ case 2:
+ return itemType == eItemTypes.TITAN_NORTHSTAR_PASSIVE
+
+ case 3:
+ return itemType == eItemTypes.TITAN_RONIN_PASSIVE
+
+ case 4:
+ return itemType == eItemTypes.TITAN_TONE_PASSIVE
+
+ case 5:
+ return itemType == eItemTypes.TITAN_LEGION_PASSIVE
+
+ case 6:
+ return itemType == eItemTypes.TITAN_VANGUARD_PASSIVE
+
+ default:
+ return false
+ }
+ }
+
+ case "passive3":
+ return (itemType == eItemTypes.TITAN_TITANFALL_PASSIVE)
+
+ case "passive4":
+ return (itemType == eItemTypes.TITAN_UPGRADE1_PASSIVE)
+ case "passive5":
+ return (itemType == eItemTypes.TITAN_UPGRADE2_PASSIVE)
+ case "passive6":
+ return (itemType == eItemTypes.TITAN_UPGRADE3_PASSIVE)
+
+ default:
+ unreachable
+ }
+
+ unreachable
+}
+
+bool function IsValidTitanExecution( int loadoutIndex, string loadoutProperty, var value, string ref ) //TODO: Not using all parameters in this function yet, might need them for full validation
+{
+ //Should have run IsRefValid(ref already, so fine to do GetItemType( ref ))
+ int itemType = GetItemType( ref )
+ //printt( "itemType: " + itemType )
+
+ switch ( loadoutProperty )
+ {
+ case "titanExecution":
+ {
+ switch( loadoutIndex ) //TODO: Hard coded, not great!
+ {
+ case 0:
+ return itemType == eItemTypes.TITAN_ION_EXECUTION
+
+ case 1:
+ return itemType == eItemTypes.TITAN_SCORCH_EXECUTION
+
+ case 2:
+ return itemType == eItemTypes.TITAN_NORTHSTAR_EXECUTION
+
+ case 3:
+ return itemType == eItemTypes.TITAN_RONIN_EXECUTION
+
+ case 4:
+ return itemType == eItemTypes.TITAN_TONE_EXECUTION
+
+ case 5:
+ return itemType == eItemTypes.TITAN_LEGION_EXECUTION
+
+ case 6:
+ return itemType == eItemTypes.TITAN_VANGUARD_EXECUTION
+
+ default:
+ return false
+ }
+ }
+
+ default:
+ unreachable
+ }
+
+ unreachable
+}
+
+string function GetParentRefFromLoadoutInfoAndRef( entity player, string loadoutType, int loadoutIndex, string property, string childRef )
+{
+ bool isPilotLoadout = (loadoutType == "pilot")
+ bool isTitanLoadout = (loadoutType == "titan")
+ if ( !isPilotLoadout && !isTitanLoadout )
+ return ""
+
+ switch ( property )
+ {
+ case "primaryAttachment":
+ case "primaryMod1":
+ case "primaryMod2":
+ case "primaryMod3":
+ case "secondaryMod1":
+ case "secondaryMod2":
+ case "secondaryMod3":
+ case "weapon3Mod1":
+ case "weapon3Mod2":
+ case "weapon3Mod3":
+ {
+ string parentProperty = GetParentLoadoutProperty( loadoutType, property )
+ string loadoutString = BuildPersistentVarAccessorString( loadoutType, loadoutIndex, parentProperty )
+ string resultString = string( player.GetPersistentVar( loadoutString ) ) //titanLoadouts[5].titanClass
+ if ( HasSubitem( resultString, childRef ) )
+ return resultString
+ else
+ return ""
+ }
+
+ case "passive1":
+ case "passive2":
+ case "passive3":
+ case "passive4":
+ case "passive5":
+ case "passive6":
+ {
+ if ( isTitanLoadout && !IsValidTitanPassive( loadoutIndex, property, childRef, childRef ) )
+ return ""
+
+ string parentProperty = GetParentLoadoutProperty( loadoutType, property )
+ string loadoutString = BuildPersistentVarAccessorString( loadoutType, loadoutIndex, parentProperty )
+ string resultString = string( player.GetPersistentVar( loadoutString ) ) //titanLoadouts[5].titanClass
+ if ( HasSubitem( resultString, childRef ) )
+ return resultString
+ else
+ return ""
+ }
+
+ case "skinIndex": //These should all only be allowed to be changed on Titans
+ case "decalIndex":
+ case "primeCamoIndex":
+ case "primeDecalIndex":
+ case "camoIndex": //For pilots, this is not a subitem. For titans, it should be titanclass.
+ {
+ if ( !isTitanLoadout )
+ return "" //Only Titans have war paints, which allow for arbitrary skin index. All others should have been taken care of earlier in AllowSkinIndexToSkipValidation()
+
+ return GetDefaultTitanLoadout( loadoutIndex ).titanClass
+ }
+
+ case "primaryCamoIndex": //For Pilots, subitem is primary. For Titans, subitem is titanclass
+ {
+ if ( isPilotLoadout )
+ {
+ string loadoutString = BuildPersistentVarAccessorString( loadoutType, loadoutIndex, "primary" )
+ return string( player.GetPersistentVar( loadoutString ) )
+ }
+ else
+ {
+ return GetDefaultTitanLoadout( loadoutIndex ).titanClass
+ }
+ }
+
+ case "secondaryCamoIndex":
+ {
+ if ( !isPilotLoadout ) //Only Pilots have secondaries
+ return ""
+
+ string loadoutString = BuildPersistentVarAccessorString( loadoutType, loadoutIndex, "secondary" )
+ return string( player.GetPersistentVar( loadoutString ) )
+ }
+
+ case "weapon3CamoIndex":
+ {
+ if ( !isPilotLoadout )
+ return ""
+
+ string loadoutString = BuildPersistentVarAccessorString( loadoutType, loadoutIndex, "weapon3" )
+ return string( player.GetPersistentVar( loadoutString ) )
+ }
+
+ case "primarySkinIndex":
+ {
+ string loadoutString = BuildPersistentVarAccessorString( loadoutType, loadoutIndex, "primary" )
+ return string( player.GetPersistentVar( loadoutString ) )
+ }
+
+ case "secondarySkinIndex":
+ {
+ if ( !isPilotLoadout ) //Only Pilots have secondaries
+ return ""
+
+ string loadoutString = BuildPersistentVarAccessorString( loadoutType, loadoutIndex, "secondary" )
+ return string( player.GetPersistentVar( loadoutString ) )
+ }
+
+ case "weapon3SkinIndex":
+ {
+ if ( !isPilotLoadout )
+ return ""
+
+ string loadoutString = BuildPersistentVarAccessorString( loadoutType, loadoutIndex, "weapon3" )
+ return string( player.GetPersistentVar( loadoutString ) )
+ }
+
+ case "primeSkinIndex": //Should be taken care of earlier in AllowSkinIndexToSkipValidation()
+ default:
+ return ""
+ }
+
+ unreachable
+}
+
+int function ForceCastVarToInt( var value ) //HACK: working around inability to cast an int to int. Reexamine when less busy!
+{
+ if ( typeof( value ) == "int" )
+ return ( expect int( value ) )
+
+ return int( value )
+}
+#endif //Server only
+
+string function GetLoadoutPropertyDefault( string loadoutType, int loadoutIndex, string propertyName ) //Bad things go wrong if you give the wrong default!
+{
+ string resultString
+ switch ( propertyName )
+ {
+ case "skinIndex":
+ case "camoIndex":
+ case "decalIndex":
+ case "primarySkinIndex":
+ case "primaryCamoIndex":
+ case "secondarySkinIndex":
+ case "secondaryCamoIndex":
+ case "weapon3SkinIndex":
+ case "weapon3CamoIndex":
+ case "primeSkinIndex":
+ case "primeCamoIndex":
+ case "primeDecalIndex":
+ resultString = "0"
+ break
+
+ case "isPrime":
+ resultString = "titan_is_not_prime"
+ break
+
+ case "execution":
+ resultString = "execution_neck_snap" //HACK, not sure where this is getting set otherwise.
+ break
+
+ case "titanExecution":
+ resultString = "execution_random_" + loadoutIndex //Loadout Index for Titans is synced to a specific Titan.
+ break
+
+ default:
+ {
+ bool isTitanLoadout = (loadoutType == "titan")
+ bool isPilotLoadout = (loadoutType == "pilot")
+ if ( isPilotLoadout )
+ {
+ PilotLoadoutDef defaultPilotLoadout = GetDefaultPilotLoadout( loadoutIndex ) //HACK: note that this can give an invalid default for a child property, e.g. a sight that doesn't exist on this weapon. This is handled later in ResolveInvalidLoadoutChildValues
+ resultString = GetPilotLoadoutValue( defaultPilotLoadout, propertyName )
+ }
+ else if ( isTitanLoadout )
+ {
+ TitanLoadoutDef defaultPilotLoadout = GetDefaultTitanLoadout( loadoutIndex ) //HACK: note that this can give an invalid default for a child property, e.g. a sight that doesn't exist on this weapon. This is handled later in ResolveInvalidLoadoutChildValues
+ resultString = GetTitanLoadoutValue( defaultPilotLoadout, propertyName )
+ }
+ else
+ {
+ unreachable
+ }
+ break
+ }
+ }
+
+ //printt( "GetLoadoutPropertyDefault, loadoutType: " + loadoutType + ", loadoutIndex: " + loadoutIndex + ", propertyName: " + propertyName + ", resultString: " + resultString )
+ return resultString
+}
+
+string function GetCategoryRestrictedResetValue( entity player, int loadoutIndex, string targetProperty, string otherProperty )
+{
+ string loadoutType = "pilot"
+ string otherPropertyValue = GetPersistentLoadoutValue( player, loadoutType, loadoutIndex, otherProperty )
+ string potentialDefaultValue = GetLoadoutPropertyDefault( loadoutType, loadoutIndex, targetProperty )
+ string resetValue = potentialDefaultValue
+
+ if ( otherPropertyValue != "" )
+ {
+ int otherCategory = expect int( GetItemDisplayData( otherPropertyValue ).i.menuCategory )
+ int potentialDefaultCategory = expect int( GetItemDisplayData( potentialDefaultValue ).i.menuCategory )
+
+ if ( potentialDefaultCategory == otherCategory )
+ {
+ Assert( otherCategory == eSecondaryWeaponCategory.AT || otherCategory == eSecondaryWeaponCategory.PISTOL )
+ if ( otherCategory == eSecondaryWeaponCategory.AT )
+ resetValue = GetWeaponCategoryBasedDefault( eSecondaryWeaponCategory.PISTOL )
+ else
+ resetValue = GetWeaponCategoryBasedDefault( eSecondaryWeaponCategory.AT )
+ }
+ }
+
+ return resetValue
+}
+
+string function GetWeaponCategoryBasedDefault( int category )
+{
+ string val
+
+ switch ( category )
+ {
+ case eSecondaryWeaponCategory.AT:
+ val = "mp_weapon_defender"
+ break
+
+ case eSecondaryWeaponCategory.PISTOL:
+ val = "mp_weapon_autopistol"
+ break
+
+ default:
+ Assert( 0, "category: " + DEV_GetEnumStringFromIndex( "eSecondaryWeaponCategory", category ) + " not handled in GetWeaponCategoryBasedDefault()" )
+ break
+ }
+
+ return val
+}
+
+string function BuildPersistentVarAccessorString( string loadoutType, int loadoutIndex, string propertyName )
+{
+ return loadoutType + "Loadouts[" + loadoutIndex + "]." + propertyName
+}
+
+bool function LoadoutIsLocked( entity player, string loadoutType, int loadoutIndex )
+{
+ if ( loadoutType == "titan" )
+ {
+ string titanClass = GetLoadoutPropertyDefault( "titan", loadoutIndex, "titanClass" )
+ return IsItemLocked( player, titanClass )
+
+ }
+ else if ( loadoutType == "pilot" )
+ {
+ string pilotLoadoutRef = "pilot_loadout_" + ( loadoutIndex + 1 )
+ return IsItemLocked( player, pilotLoadoutRef )
+ }
+
+ unreachable
+
+}
+
+string function GetValidatedPersistentLoadoutValue( entity player, string loadoutType, int loadoutIndex, string loadoutProperty )
+{
+ #if SERVER
+ Assert( loadoutType == "pilot" || loadoutType == "titan" )
+ Assert( loadoutIndex < PersistenceGetArrayCount( loadoutType + "Loadouts" ), "Invalid loadoutIndex: " + loadoutIndex )
+
+ var loadoutPropertyEnum = null
+ if ( loadoutType == "pilot" )
+ {
+ bool isValidPilotLoadoutProperty = IsValidPilotLoadoutProperty( loadoutProperty )
+ //Assert( isValidPilotLoadoutProperty, "Invalid pilot loadoutProperty: " + loadoutProperty )
+ if( !isValidPilotLoadoutProperty )
+ {
+ CodeWarning( "Invalid pilot loadoutProperty: " + loadoutProperty )
+ return ""
+ }
+
+ loadoutPropertyEnum = GetPilotLoadoutPropertyEnum( loadoutProperty )
+ }
+ else
+ {
+ bool isValidTitanLoadoutProperty = IsValidTitanLoadoutProperty( loadoutProperty )
+ //Assert( isValidTitanLoadoutProperty, "Invalid titan loadoutProperty: " + loadoutProperty )
+ if ( !isValidTitanLoadoutProperty )
+ {
+ CodeWarning( "Invalid titan loadoutProperty: " + loadoutProperty )
+ return ""
+ }
+ loadoutPropertyEnum = GetTitanLoadoutPropertyEnum( loadoutProperty )
+ }
+
+ string getString = BuildPersistentVarAccessorString( loadoutType, loadoutIndex, loadoutProperty )
+ var value = player.GetPersistentVar( getString )
+
+ if ( value == null )
+ value = ""
+
+ if ( loadoutPropertyEnum != null )
+ {
+ if ( !PersistenceEnumValueIsValid( loadoutPropertyEnum, value ) )
+ {
+ printt( "Invalid Loadout Property: ", loadoutType, loadoutIndex, loadoutProperty, value )
+ value = ResetLoadoutPropertyToDefault( player, loadoutType, loadoutIndex, loadoutProperty ) //TODO: This will call player.SetPersistentVar() directly. Awkward to do this in a getter function
+ ClientCommand( player, "disconnect #RESETTING_LOADOUT", 0 ) //Kick player out with a "Resetting Invalid Loadout" message. Mainly necessary so UI/Client script don't crash out later with known, bad data from persistence
+ }
+ }
+
+ if ( LoadoutPropertyRequiresItemValidation( loadoutProperty ) && value != "" )
+ {
+ if ( FailsLoadoutValidationCheck( player, loadoutType, loadoutIndex, loadoutProperty, value ) )
+ {
+ printt( "Invalid Loadout Property: ", loadoutType, loadoutIndex, loadoutProperty, value )
+ value = ResetLoadoutPropertyToDefault( player, loadoutType, loadoutIndex, loadoutProperty ) //TODO: This will call player.SetPersistentVar() directly. Awkward to do this in a getter function
+ ClientCommand( player, "disconnect #RESETTING_LOADOUT", 0 ) //Kick player out with a "Resetting Invalid Loadout" message. Mainly necessary so UI/Client script don't crash out later with known, bad data from persistence
+ }
+
+ ValidateSkinAndCamoIndexesAsAPair( player, loadoutType, loadoutIndex, loadoutProperty, value ) //TODO: This is awkward, has the potential to call a SetPersistentLoadoutValue() if skinIndex and camoIndex are not correct as a pair
+ }
+
+ return string( value )
+ #else
+ return GetPersistentLoadoutValue( player, loadoutType, loadoutIndex, loadoutProperty )
+ #endif
+}
+
+int function GetValidatedPersistentLoadoutValueInt( entity player, string loadoutType, int loadoutIndex, string loadoutProperty )
+{
+ #if SERVER
+ string returnValue = GetValidatedPersistentLoadoutValue( player, loadoutType, loadoutIndex, loadoutProperty )
+ return int( returnValue )
+ #else
+ return GetPersistentLoadoutValueInt( player, loadoutType, loadoutIndex, loadoutProperty )
+ #endif
+}
+
+
+// SERVER: Looks at and updates persistent loadout data
+// CLIENT/UI: Looks at and updates cached loadout data
+void function ResolveInvalidLoadoutChildValues( entity player, string loadoutType, int loadoutIndex, string loadoutProperty, string parentValue )
+{
+ array<string> childProperties = GetChildLoadoutProperties( loadoutType, loadoutProperty )
+
+ //if ( childProperties.len() )
+ //{
+ // printt( "====== loadoutProperty:", loadoutProperty, "childProperties are:" )
+ // foreach ( childProperty in childProperties )
+ // printt( "====== ", childProperty )
+ //}
+
+ if ( childProperties.len() )
+ {
+ Assert( parentValue != "" )
+
+ foreach ( childProperty in childProperties )
+ {
+ string childValue
+
+ if ( childProperty == "primaryCamoIndex" || childProperty == "secondaryCamoIndex" || childProperty == "weapon3CamoIndex" ) // TODO: Keep skin on weapon change if possible
+ {
+ //printt( "====== loadoutProperty:", loadoutProperty, "childProperty:", childProperty, "will blind RESET" )
+ }
+ else if ( childProperty == "primarySkinIndex" || childProperty == "secondarySkinIndex" || childProperty == "weapon3SkinIndex" )
+ {
+ //printt( "====== loadoutProperty:", loadoutProperty, "childProperty:", childProperty, "will blind RESET" )
+ }
+ else if ( (childProperty == "primaryMod2" || childProperty == "primaryMod3") && IsSubItemLocked( player, childProperty.tolower(), parentValue ) ) // check primarymod2 or primarymod3 feature locked
+ {
+ //printt( "====== loadoutProperty:", loadoutProperty, "childProperty:", childProperty, "will RESET due to feature lock" )
+ }
+ else if ( (childProperty == "secondaryMod2" || childProperty == "weapon3Mod2") && IsSubItemLocked( player, "secondarymod2", parentValue ) ) // check secondarymod2 feature locked
+ {
+ //printt( "====== loadoutProperty:", loadoutProperty, "childProperty:", childProperty, "will RESET due to feature lock" )
+ }
+ else if ( (childProperty == "secondaryMod3" || childProperty == "weapon3Mod3") && IsSubItemLocked( player, "secondarymod3", parentValue ) ) // check secondarymod3 feature locked
+ {
+ //printt( "====== loadoutProperty:", loadoutProperty, "childProperty:", childProperty, "will RESET due to feature lock" )
+ }
+ else // check subitem locked
+ {
+ #if SERVER
+ // TODO: This call will actually check and change the persistent data if it's invalid. Doesn't break anything here, but this function really shouldn't change persistent data.
+ childValue = GetPersistentLoadoutValue( player, loadoutType, loadoutIndex, childProperty )
+ #else
+ if ( loadoutType == "pilot" )
+ childValue = GetPilotLoadoutValue( shGlobal.cachedPilotLoadouts[ loadoutIndex ], childProperty )
+ else if ( loadoutType == "titan" )
+ childValue = GetTitanLoadoutValue( shGlobal.cachedTitanLoadouts[ loadoutIndex ], childProperty )
+ else
+ CodeWarning( "Unhandled loadoutType: " + loadoutType )
+ #endif
+
+ if ( childValue == "" || ( childValue != "" && HasSubitem( parentValue, childValue ) && !IsSubItemLocked( player, childValue, parentValue ) ) )
+ {
+ //printt( "====== loadoutProperty:", loadoutProperty, "childProperty:", childProperty, "will KEEP childValue: \"" + childValue + "\"" )
+ continue
+ }
+ //else
+ //{
+ // printt( "====== loadoutProperty:", loadoutProperty, "childProperty:", childProperty, "will RESET childValue: \"" + childValue + "\"" )
+ //}
+ }
+
+ childValue = GetLoadoutChildPropertyDefault( loadoutType, childProperty, parentValue )
+
+ #if SERVER
+ SetPlayerPersistentVarWithoutValidation( player, loadoutType, loadoutIndex, childProperty, childValue )
+ #else
+ if ( loadoutType == "pilot" )
+ SetPilotLoadoutValue( shGlobal.cachedPilotLoadouts[ loadoutIndex ], childProperty, childValue )
+ else if ( loadoutType == "titan" )
+ SetTitanLoadoutValue( shGlobal.cachedTitanLoadouts[ loadoutIndex ], childProperty, childValue )
+ else
+ CodeWarning( "Unhandled loadoutType: " + loadoutType )
+ #endif
+ }
+ }
+ //else
+ //{
+ // printt( "====== loadoutProperty:", loadoutProperty, "childProperties is empty" )
+ //}
+}
+
+#if UI || CLIENT
+void function SetCachedPilotLoadoutValue( entity player, int loadoutIndex, string loadoutProperty, string value )
+{
+ if ( !IsValidPilotLoadoutProperty( loadoutProperty ) )
+ {
+ CodeWarning( "Tried to set pilot " + loadoutProperty + " to invalid value: " + value )
+ return
+ }
+
+ // Reset child properties when parent changes
+ ResolveInvalidLoadoutChildValues( player, "pilot", loadoutIndex, loadoutProperty, value )
+
+ #if HAS_THREAT_SCOPE_SLOT_LOCK
+ if ( loadoutProperty.tolower() == "primaryattachment" && value == "threat_scope" )
+ {
+ SetPilotLoadoutValue( shGlobal.cachedPilotLoadouts[ loadoutIndex ], "primaryMod2", "" )
+ }
+ #endif
+
+ SetPilotLoadoutValue( shGlobal.cachedPilotLoadouts[ loadoutIndex ], loadoutProperty, value )
+ UpdateDerivedPilotLoadoutData( shGlobal.cachedPilotLoadouts[ loadoutIndex ] )
+
+ #if UI
+ if ( value == "" )
+ value = "null"
+ ClientCommand( "SetPersistentLoadoutValue pilot " + loadoutIndex + " " + loadoutProperty + " " + value )
+ #endif // UI
+}
+
+
+void function SetCachedTitanLoadoutValue( entity player, int loadoutIndex, string loadoutProperty, string value )
+{
+ if ( !IsValidTitanLoadoutProperty( loadoutProperty ) )
+ {
+ CodeWarning( "Tried to set titan " + loadoutProperty + " to invalid value: " + value )
+ return
+ }
+
+ // Reset child properties when parent changes
+ ResolveInvalidLoadoutChildValues( player, "titan", loadoutIndex, loadoutProperty, value )
+
+ SetTitanLoadoutValue( shGlobal.cachedTitanLoadouts[ loadoutIndex ], loadoutProperty, value )
+ UpdateDerivedTitanLoadoutData( shGlobal.cachedTitanLoadouts[ loadoutIndex ] )
+
+ #if UI
+ if ( value == "" )
+ value = "null"
+ ClientCommand( "SetPersistentLoadoutValue titan " + loadoutIndex + " " + loadoutProperty + " " + value )
+ #endif // UI
+}
+
+
+
+// TODO: If we change a property that has a parent or child relationship, all related properties need updating if invalid
+// A parent change should validate children and set invalid to defaults
+// If a child change is invalid for the parent, it should be changed to a valid default based on the parent
+void function SetCachedLoadoutValue( entity player, string loadoutType, int loadoutIndex, string loadoutProperty, string value )
+{
+ // Keep CLIENT matching UI
+ #if UI
+ RunClientScript( "SetCachedLoadoutValue", player, loadoutType, loadoutIndex, loadoutProperty, value )
+ #endif // UI
+
+ //printt( "=======================================================================================" )
+ //printt( "SetPersistentLoadoutValue called with loadoutType:", loadoutType, "loadoutIndex:", loadoutIndex, "loadoutProperty:" , loadoutProperty, "value:", value )
+ //printl( "script GetPlayerArray()[0].SetPersistentVar( \"" + loadoutType + "Loadouts[" + loadoutIndex + "]." + loadoutProperty + "\", \"" + value + "\" )" )
+ //printt( "=======================================================================================" )
+
+ if ( loadoutType == "pilot" )
+ SetCachedPilotLoadoutValue( player, loadoutIndex, loadoutProperty, value )
+ else if ( loadoutType == "titan" )
+ SetCachedTitanLoadoutValue( player, loadoutIndex, loadoutProperty, value )
+ else
+ CodeWarning( "Unhandled loadoutType: " + loadoutType )
+
+ /*
+ Assert( loadoutType == "pilot" || loadoutType == "titan" )
+ Assert( loadoutIndex < PersistenceGetArrayCount( loadoutType + "Loadouts" ), "Invalid loadoutIndex: " + loadoutIndex )
+
+ if ( value == "null" || value == "" || value == null )
+ {
+ CodeWarning( "Tried to set " + loadoutType + " " + loadoutProperty + " to invalid value: " + value )
+ return
+ }
+
+ var loadoutPropertyEnum = null
+ if ( loadoutType == "pilot" )
+ {
+ if ( !IsValidPilotLoadoutProperty( loadoutProperty ) )
+ {
+ CodeWarning( "Invalid pilot loadoutProperty: " + loadoutProperty )
+ return
+ }
+
+ loadoutPropertyEnum = GetPilotLoadoutPropertyEnum( loadoutProperty )
+ }
+ else
+ {
+ if ( !IsValidTitanLoadoutProperty( loadoutProperty ) )
+ {
+ CodeWarning( "Invalid titan loadoutProperty: " + loadoutProperty )
+ return
+ }
+
+ loadoutPropertyEnum = GetTitanLoadoutPropertyEnum( loadoutProperty )
+ }
+
+ if ( loadoutPropertyEnum == null )
+ {
+ CodeWarning( "Couldn't find loadoutPropertyEnum for " + loadoutType + " " + loadoutProperty )
+ return
+ }
+
+ if ( !PersistenceEnumValueIsValid( loadoutPropertyEnum, value ) )
+ {
+ CodeWarning( loadoutProperty + " value: " + value + " not valid in persistent data!" )
+ return
+ }
+
+ // Only checks primary mods and attachments are valid in itemData and the parent isn't locked
+ if ( !IsLoadoutSubitemValid( player, loadoutType, loadoutIndex, loadoutProperty, value ) )
+ {
+ CodeWarning( loadoutProperty + " value: " + value + " failed IsLoadoutSubitemValid() check! It may not exist in itemData, or it's parent item is locked." )
+ return
+ }
+
+ // Reset child properties when parent changes
+ array<string> childProperties = GetChildLoadoutProperties( loadoutType, loadoutProperty )
+ if ( childProperties.len() )
+ {
+ foreach ( childProperty in childProperties )
+ {
+ Assert( value != "" )
+ string childValue = GetLoadoutChildPropertyDefault( loadoutType, childProperty, value )
+
+ if ( loadoutType == "pilot" )
+ SetPilotLoadoutValue( shGlobal.cachedPilotLoadouts[ loadoutIndex ], childProperty, childValue )
+ else
+ SetTitanLoadoutValue( shGlobal.cachedTitanLoadouts[ loadoutIndex ], childProperty, childValue )
+ }
+ }
+
+ if ( loadoutType == "pilot" )
+ {
+ SetPilotLoadoutValue( shGlobal.cachedPilotLoadouts[ loadoutIndex ], loadoutProperty, value )
+ UpdateDerivedPilotLoadoutData( shGlobal.cachedPilotLoadouts[ loadoutIndex ] )
+ }
+ else
+ {
+ SetTitanLoadoutValue( shGlobal.cachedTitanLoadouts[ loadoutIndex ], loadoutProperty, value )
+ OverwriteLoadoutWithDefaultsForSetFile_ExceptSpecialAndAntiRodeo( shGlobal.cachedTitanLoadouts[ loadoutIndex ] )
+ }
+
+ if ( value == "" || value == "null" )
+ ClientCommand( "SetPersistentLoadoutValue " + loadoutType + " " + loadoutIndex + " " + loadoutProperty + " " + null )
+ else
+ ClientCommand( "SetPersistentLoadoutValue " + loadoutType + " " + loadoutIndex + " " + loadoutProperty + " " + value )
+ */
+}
+#endif // UI || CLIENT
+
+// SERVER version waits till end of frame to reduce remote calls which can get high when loadouts are first initialized
+// CLIENT and UI versions need to wait for persistent data to be ready
+// Ideally, UI and CLIENT would have a callback that triggered when persistent loadout data changed.
+void function UpdateCachedLoadouts()
+{
+ #if SERVER
+ Signal( level, "EndUpdateCachedLoadouts" )
+ EndSignal( level, "EndUpdateCachedLoadouts" )
+
+ WaitEndFrame()
+
+ array<entity> players = GetPlayerArray()
+
+ foreach ( player in players )
+ {
+ if ( player.p.pilotLoadoutChanged )
+ {
+ Remote_CallFunction_NonReplay( player, "UpdateAllCachedPilotLoadouts" )
+ player.p.pilotLoadoutChanged = false
+ }
+
+ if ( player.p.titanLoadoutChanged )
+ {
+ Remote_CallFunction_NonReplay( player, "UpdateAllCachedTitanLoadouts" )
+ player.p.titanLoadoutChanged = false
+ }
+
+ // TEMP client model update method
+ if ( player.p.pilotModelNeedsUpdate != -1 )
+ {
+ Remote_CallFunction_NonReplay( player, "ServerCallback_UpdatePilotModel", player.p.pilotModelNeedsUpdate )
+ player.p.pilotModelNeedsUpdate = -1
+ }
+
+ if ( player.p.titanModelNeedsUpdate != -1 )
+ {
+ Remote_CallFunction_NonReplay( player, "ServerCallback_UpdateTitanModel", player.p.titanModelNeedsUpdate )
+ player.p.titanModelNeedsUpdate = -1
+ }
+ }
+ #elseif UI || CLIENT
+ #if UI
+ entity player = GetUIPlayer()
+ #elseif CLIENT
+ entity player = GetLocalClientPlayer()
+ #endif
+
+ if ( player == null )
+ return
+
+ #if UI
+ EndSignal( uiGlobal.signalDummy, "LevelShutdown" )
+ #endif // UI
+
+ while ( player.GetPersistentVarAsInt( "initializedVersion" ) < PERSISTENCE_INIT_VERSION )
+ {
+ WaitFrame()
+ }
+
+ UpdateAllCachedPilotLoadouts()
+ UpdateAllCachedTitanLoadouts()
+ #if CLIENT
+ Signal( level, "CachedLoadoutsReady" )
+ #endif // CLIENT
+ #endif
+}
+
+#if UI
+ void function InitUISpawnLoadoutIndexes()
+ {
+ EndSignal( uiGlobal.signalDummy, "LevelShutdown" )
+
+ entity player = GetUIPlayer()
+
+ if ( player == null )
+ return
+
+ while ( player.GetPersistentVarAsInt( "initializedVersion" ) < PERSISTENCE_INIT_VERSION )
+ WaitFrame()
+
+ uiGlobal.pilotSpawnLoadoutIndex = GetPersistentSpawnLoadoutIndex( player, "pilot" )
+ uiGlobal.titanSpawnLoadoutIndex = GetPersistentSpawnLoadoutIndex( player, "titan" )
+ }
+#endif // UI
+
+// There's no good way to dynamically reference a struct variable
+string function GetPilotLoadoutValue( PilotLoadoutDef loadout, string property )
+{
+ string value
+ printt( "GetPilotLoadoutValue, property: " + property )
+
+ switch ( property )
+ {
+ case "name":
+ value = loadout.name
+ break
+
+ case "suit":
+ value = loadout.suit
+ break
+
+ case "race":
+ value = loadout.race
+ break
+
+ case "primary":
+ value = loadout.primary
+ break
+
+ case "primaryAttachment":
+ value = loadout.primaryAttachment
+ break
+
+ case "primaryMod1":
+ value = loadout.primaryMod1
+ break
+
+ case "primaryMod2":
+ value = loadout.primaryMod2
+ break
+
+ case "primaryMod3":
+ value = loadout.primaryMod3
+ break
+
+ case "secondary":
+ value = loadout.secondary
+ break
+
+ case "secondaryMod1":
+ value = loadout.secondaryMod1
+ break
+
+ case "secondaryMod2":
+ value = loadout.secondaryMod2
+ break
+
+ case "secondaryMod3":
+ value = loadout.secondaryMod3
+ break
+
+ case "weapon3":
+ value = loadout.weapon3
+ break
+
+ case "weapon3Mod1":
+ value = loadout.weapon3Mod1
+ break
+
+ case "weapon3Mod2":
+ value = loadout.weapon3Mod2
+ break
+
+ case "weapon3Mod3":
+ value = loadout.weapon3Mod3
+ break
+
+ case "special":
+ value = loadout.special
+ break
+
+ case "ordnance":
+ value = loadout.ordnance
+ break
+
+ case "passive1":
+ value = loadout.passive1
+ break
+
+ case "passive2":
+ value = loadout.passive2
+ break
+
+ case "melee":
+ value = loadout.melee
+ break
+
+ case "execution":
+ value = loadout.execution
+ break
+
+ case "skinIndex":
+ value = string( loadout.skinIndex )
+ break
+
+ case "camoIndex":
+ value = string( loadout.camoIndex )
+ break
+
+ case "primarySkinIndex":
+ value = string( loadout.primarySkinIndex )
+ break
+
+ case "primaryCamoIndex":
+ value = string( loadout.primaryCamoIndex )
+ break
+
+ case "secondarySkinIndex":
+ value = string( loadout.secondarySkinIndex )
+ break
+
+ case "secondaryCamoIndex":
+ value = string ( loadout.secondaryCamoIndex )
+ break
+
+ case "weapon3SkinIndex":
+ value = string( loadout.weapon3SkinIndex )
+ break
+
+ case "weapon3CamoIndex":
+ value = string ( loadout.weapon3CamoIndex )
+ break
+
+ default:
+ printt( "Returning blank for property: " + property ) //TODO: This should probably just be an error, but some existing calls depend on blank string being returned
+ value = ""
+ }
+
+ return value
+}
+
+// There's no good way to dynamically reference a struct variable
+// If this starts getting called outside of SetPersistentLoadoutValue(), it needs error checking
+void function SetPilotLoadoutValue( PilotLoadoutDef loadout, string property, string value )
+{
+ switch ( property )
+ {
+ case "name":
+ loadout.name = value
+ break
+
+ case "suit":
+ loadout.suit = value
+ break
+
+ case "execution":
+ loadout.execution = value
+ break
+
+ case "race":
+ loadout.race = value
+ break
+
+ case "primary":
+ loadout.primary = value
+ break
+
+ case "primaryAttachment":
+ loadout.primaryAttachment = value
+ break
+
+ case "primaryMod1":
+ loadout.primaryMod1 = value
+ break
+
+ case "primaryMod2":
+ loadout.primaryMod2 = value
+ break
+
+ case "primaryMod3":
+ loadout.primaryMod3 = value
+ break
+
+ case "secondary":
+ loadout.secondary = value
+ break
+
+ case "secondaryMod1":
+ loadout.secondaryMod1 = value
+ break
+
+ case "secondaryMod2":
+ loadout.secondaryMod2 = value
+ break
+
+ case "secondaryMod3":
+ loadout.secondaryMod3 = value
+ break
+
+ case "weapon3":
+ loadout.weapon3 = value
+ break
+
+ case "weapon3Mod1":
+ loadout.weapon3Mod1 = value
+ break
+
+ case "weapon3Mod2":
+ loadout.weapon3Mod2 = value
+ break
+
+ case "weapon3Mod3":
+ loadout.weapon3Mod3 = value
+ break
+
+ case "special":
+ loadout.special = value
+ break
+
+ case "ordnance":
+ loadout.ordnance = value
+ break
+
+ case "passive1":
+ loadout.passive1 = value
+ break
+
+ case "passive2":
+ loadout.passive2 = value
+ break
+
+ case "melee":
+ loadout.melee = value
+ break
+
+ case "skinIndex":
+ loadout.skinIndex = int( value )
+ break
+
+ case "camoIndex":
+ loadout.camoIndex = int( value )
+ break
+
+ case "primarySkinIndex":
+ loadout.primarySkinIndex = int( value )
+ break
+
+ case "primaryCamoIndex":
+ loadout.primaryCamoIndex = int( value )
+ break
+
+ case "secondarySkinIndex":
+ loadout.secondarySkinIndex = int( value )
+ break
+
+ case "secondaryCamoIndex":
+ loadout.secondaryCamoIndex = int( value )
+ break
+
+ case "weapon3SkinIndex":
+ loadout.weapon3SkinIndex = int( value )
+ break
+
+ case "weapon3CamoIndex":
+ loadout.weapon3CamoIndex = int( value )
+ break
+ }
+}
+
+// There's no good way to dynamically reference a struct variable
+string function GetTitanLoadoutValue( TitanLoadoutDef loadout, string property )
+{
+ string value
+
+ switch ( property )
+ {
+ case "name":
+ value = loadout.name
+ break
+
+ case "titanClass":
+ value = loadout.titanClass
+ break
+
+ case "primeTitanRef":
+ value = loadout.primeTitanRef
+ break
+
+ case "setFile":
+ value = loadout.setFile
+ break
+
+ case "coreAbility":
+ value = loadout.coreAbility
+ break
+
+ case "primary":
+ value = loadout.primary
+ break
+
+ case "primaryMod":
+ value = loadout.primaryMod
+ break
+
+ case "special":
+ value = loadout.special
+ break
+
+ case "antirodeo":
+ value = loadout.antirodeo
+ break
+
+ case "ordnance":
+ value = loadout.ordnance
+ break
+
+ case "passive1":
+ value = loadout.passive1
+ break
+
+ case "passive2":
+ value = loadout.passive2
+ break
+
+ case "passive3":
+ value = loadout.passive3
+ break
+
+ case "passive4":
+ value = loadout.passive4
+ break
+
+ case "passive5":
+ value = loadout.passive5
+ break
+
+ case "passive6":
+ value = loadout.passive6
+ break
+
+ case "skinIndex":
+ value = string( loadout.skinIndex )
+ break
+
+ case "camoIndex":
+ value = string ( loadout.camoIndex )
+ break
+
+ case "decalIndex":
+ value = string( loadout.decalIndex )
+ break
+
+ case "primarySkinIndex":
+ value = string ( loadout.primarySkinIndex )
+ break
+
+ case "primaryCamoIndex":
+ value = string ( loadout.primaryCamoIndex )
+ break
+
+ case "difficulty":
+ value = string ( loadout.difficulty ) //TODO: Unused?
+ break
+
+ case "isPrime":
+ value = loadout.isPrime
+ break
+
+ case "primeSkinIndex":
+ value = string( loadout.primeSkinIndex )
+ break
+
+ case "primeCamoIndex":
+ value = string( loadout.primeCamoIndex )
+ break
+
+ case "primeDecalIndex":
+ value = string( loadout.primeDecalIndex )
+ break
+
+ case "titanExecution":
+ value = loadout.titanExecution
+ break
+
+ default:
+ printt( "Returning blank for property: " + property ) //TODO: This should probably just be an error, but some existing calls depend on blank string being returned
+ value = ""
+ }
+
+ return value
+}
+
+// There's no good way to dynamically reference a struct variable
+// If this starts getting called outside of SetPersistentLoadoutValue(), it needs error checking
+void function SetTitanLoadoutValue( TitanLoadoutDef loadout, string property, string value )
+{
+ switch ( property )
+ {
+ case "name":
+ loadout.name = value
+ break
+
+ case "setFile":
+ loadout.setFile = value
+ break
+
+ case "primaryMod":
+ loadout.primaryMod = value
+ break
+
+ case "special":
+ loadout.special = value
+ break
+
+ case "antirodeo":
+ loadout.antirodeo = value
+ break
+
+ case "passive1":
+ loadout.passive1 = value
+ break
+
+ case "passive2":
+ loadout.passive2 = value
+ break
+
+ case "passive3":
+ loadout.passive3 = value
+ break
+
+ case "passive4":
+ loadout.passive4 = value
+ break
+
+ case "passive5":
+ loadout.passive5 = value
+ break
+
+ case "passive6":
+ loadout.passive6 = value
+ break
+
+ case "skinIndex":
+ loadout.skinIndex = int( value )
+ break
+
+ case "camoIndex":
+ loadout.camoIndex = int( value )
+ break
+
+ case "decalIndex":
+ loadout.decalIndex = int( value )
+ break
+
+ case "primarySkinIndex":
+ loadout.primarySkinIndex = int( value )
+ break
+
+ case "primaryCamoIndex":
+ loadout.primaryCamoIndex = int( value )
+ break
+
+ case "isPrime":
+ loadout.isPrime = value
+ break
+
+ case "primeSkinIndex":
+ loadout.primeSkinIndex = int( value )
+ break
+
+ case "primeCamoIndex":
+ loadout.primeCamoIndex = int( value )
+ break
+
+ case "primeDecalIndex":
+ loadout.primeDecalIndex = int( value )
+ break
+
+ case "titanExecution":
+ loadout.titanExecution = value
+ break
+
+ case "showArmBadge":
+ loadout.showArmBadge = int( value )
+ break
+ }
+}
+
+bool function IsValidPilotLoadoutProperty( string propertyName )
+{
+ switch ( propertyName )
+ {
+ case "name":
+ case "suit":
+ case "race":
+ case "execution":
+ case "primary":
+ case "primaryAttachment":
+ case "primaryMod1":
+ case "primaryMod2":
+ case "primaryMod3":
+ case "secondary":
+ case "secondaryMod1":
+ case "secondaryMod2":
+ case "secondaryMod3":
+ case "weapon3":
+ case "weapon3Mod1":
+ case "weapon3Mod2":
+ case "weapon3Mod3":
+ case "ordnance":
+ case "special":
+ case "passive1":
+ case "passive2":
+ case "melee":
+ case "skinIndex":
+ case "camoIndex":
+ case "primarySkinIndex":
+ case "primaryCamoIndex":
+ case "secondarySkinIndex":
+ case "secondaryCamoIndex":
+ case "weapon3SkinIndex":
+ case "weapon3CamoIndex":
+ return true
+ }
+
+ return false
+}
+
+bool function IsValidTitanLoadoutProperty( string propertyName )
+{
+ switch ( propertyName )
+ {
+ case "name":
+ case "titanClass":
+ case "setFile":
+ case "primaryMod":
+ case "special":
+ case "antirodeo":
+ case "passive1":
+ case "passive2":
+ case "passive3":
+ case "passive4":
+ case "passive5":
+ case "passive6":
+ case "skinIndex":
+ case "camoIndex":
+ case "decalIndex":
+ case "primarySkinIndex":
+ case "primaryCamoIndex":
+ case "isPrime":
+ case "primeSkinIndex":
+ case "primeCamoIndex":
+ case "primeDecalIndex":
+ case "titanExecution":
+ case "showArmBadge":
+ return true
+ }
+
+ return false
+}
+
+var function GetPilotLoadoutPropertyEnum( string property )
+{
+ switch ( property )
+ {
+ case "suit":
+ return "pilotSuit"
+
+ case "race":
+ return "pilotRace"
+
+ case "primary":
+ case "secondary":
+ case "weapon3":
+ case "special":
+ case "ordnance":
+ case "melee":
+ return "loadoutWeaponsAndAbilities"
+
+ case "primaryAttachment":
+ case "primaryMod1":
+ case "primaryMod2":
+ case "primaryMod3":
+ case "secondaryMod1":
+ case "secondaryMod2":
+ case "secondaryMod3":
+ case "weapon3Mod1":
+ case "weapon3Mod2":
+ case "weapon3Mod3":
+ return "pilotMod"
+
+ case "passive1":
+ case "passive2":
+ return "pilotPassive"
+
+ case "name":
+ default:
+ return null
+ }
+}
+
+var function GetTitanLoadoutPropertyEnum( string property )
+{
+ switch ( property )
+ {
+ case "titanClass":
+ return "titanClasses"
+ case "primary":
+ case "special":
+ case "antirodeo":
+ case "ordnance":
+ return "loadoutWeaponsAndAbilities"
+ case "primaryMod":
+ return "titanMod"
+ case "passive1":
+ case "passive2":
+ case "passive3":
+ case "passive4":
+ case "passive5":
+ case "passive6":
+ return "titanPassive"
+ case "isPrime":
+ return "titanIsPrimeTitan"
+ case "name":
+ default:
+ return null
+ }
+}
+
+int function GetItemTypeFromPilotLoadoutProperty( string loadoutProperty )
+{
+ int itemType
+
+ switch ( loadoutProperty )
+ {
+ case "suit":
+ itemType = eItemTypes.PILOT_SUIT
+ break
+
+ case "primary":
+ itemType = eItemTypes.PILOT_PRIMARY
+ break
+
+ case "secondary":
+ case "weapon3": // Most cases need to treat as secondary, so don't return eItemTypes.PILOT_WEAPON3 here
+ itemType = eItemTypes.PILOT_SECONDARY
+ break
+
+ case "special":
+ itemType = eItemTypes.PILOT_SPECIAL
+ break
+
+ case "ordnance":
+ itemType = eItemTypes.PILOT_ORDNANCE
+ break
+
+ case "passive1":
+ itemType = eItemTypes.PILOT_PASSIVE1
+ break
+
+ case "passive2":
+ itemType = eItemTypes.PILOT_PASSIVE2
+ break
+
+ case "primaryAttachment":
+ itemType = eItemTypes.PILOT_PRIMARY_ATTACHMENT
+ break
+
+ case "primaryMod1":
+ case "primaryMod2":
+ itemType = eItemTypes.PILOT_PRIMARY_MOD
+ break
+
+ case "secondaryMod1":
+ case "secondaryMod2":
+ case "weapon3Mod1":
+ case "weapon3Mod2":
+ itemType = eItemTypes.PILOT_SECONDARY_MOD
+ break
+
+ case "primaryMod3":
+ case "secondaryMod3":
+ case "weapon3Mod3":
+ itemType = eItemTypes.PILOT_WEAPON_MOD3
+ break
+
+ case "race":
+ itemType = eItemTypes.RACE
+ break
+
+ case "execution":
+ itemType = eItemTypes.PILOT_EXECUTION
+ break
+
+ default:
+ Assert( false, "Invalid pilot loadout property!" )
+ }
+
+ return itemType
+}
+
+int function GetItemTypeFromTitanLoadoutProperty( string loadoutProperty, string setFile = "" )
+{
+ int itemType
+
+ switch ( loadoutProperty )
+ {
+ case "setFile":
+ itemType = eItemTypes.TITAN
+ break
+
+ case "coreAbility":
+ itemType = eItemTypes.TITAN_CORE_ABILITY
+ break
+
+ case "primary":
+ itemType = eItemTypes.TITAN_PRIMARY
+ break
+
+ case "special":
+ itemType = eItemTypes.TITAN_SPECIAL
+ break
+
+ case "ordnance":
+ itemType = eItemTypes.TITAN_ORDNANCE
+ break
+
+ case "antirodeo":
+ itemType = eItemTypes.TITAN_ANTIRODEO
+ break
+
+ case "passive1":
+ case "passive2":
+ case "passive3":
+ case "passive4":
+ case "passive5":
+ case "passive6":
+ Assert( setFile != "" )
+ itemType = GetTitanLoadoutPropertyPassiveType( setFile, loadoutProperty )
+ break
+
+ case "titanExecution":
+ Assert( setFile != "" )
+ itemType = GetTitanLoadoutPropertyExecutionType( setFile, loadoutProperty )
+ break
+
+ case "voice":
+ itemType = eItemTypes.TITAN_OS
+ break
+
+ case "primaryMod":
+ itemType = eItemTypes.TITAN_PRIMARY_MOD
+ break
+
+ default:
+ Assert( false, "Invalid titan loadout property!" )
+ }
+
+ return itemType
+}
+
+string function GetLoadoutChildPropertyDefault( string loadoutType, string propertyName, string parentValue )
+{
+ Assert( loadoutType == "pilot" || loadoutType == "titan" )
+
+ string resetValue = ""
+
+ if ( loadoutType == "pilot" )
+ {
+ switch ( propertyName )
+ {
+ case "primaryAttachment":
+ case "primaryMod1":
+ case "primaryMod2":
+ case "primaryMod3":
+ case "secondaryMod1":
+ case "secondaryMod2":
+ case "secondaryMod3":
+ case "weapon3Mod1":
+ case "weapon3Mod2":
+ case "weapon3Mod3":
+ resetValue = GetWeaponBasedDefaultMod( parentValue, propertyName )
+ break
+
+ case "primaryCamoIndex":
+ case "primarySkinIndex":
+ case "secondaryCamoIndex":
+ case "secondarySkinIndex":
+ case "weapon3CamoIndex":
+ case "weapon3SkinIndex":
+ resetValue = "0"
+ break
+ }
+ }
+
+ return resetValue
+}
+
+// There's no great way to know the dependency heirarchy of persistent loadout data
+array<string> function GetChildLoadoutProperties( string loadoutType, string propertyName )
+{
+ Assert( loadoutType == "pilot" || loadoutType == "titan" )
+
+ array<string> childProperties
+
+ if ( loadoutType == "pilot" )
+ {
+ switch ( propertyName )
+ {
+ /*case "suit":
+ childProperties.append( "passive1" )
+ childProperties.append( "passive2" )
+ break*/
+
+ case "primary":
+ childProperties.append( "primaryAttachment" )
+ childProperties.append( "primaryMod1" )
+ childProperties.append( "primaryMod2" )
+ childProperties.append( "primaryMod3" )
+ childProperties.append( "primaryCamoIndex" )
+ childProperties.append( "primarySkinIndex" )
+ break
+
+ case "secondary":
+ childProperties.append( "secondaryMod1" )
+ childProperties.append( "secondaryMod2" )
+ childProperties.append( "secondaryMod3" )
+ childProperties.append( "secondaryCamoIndex" )
+ childProperties.append( "secondarySkinIndex" )
+ break
+
+ case "weapon3":
+ childProperties.append( "weapon3Mod1" )
+ childProperties.append( "weapon3Mod2" )
+ childProperties.append( "weapon3Mod3" )
+ childProperties.append( "weapon3CamoIndex" )
+ childProperties.append( "weapon3SkinIndex" )
+ break
+
+ /*case "melee":
+ childProperties.append( "meleeMods" ) // not in persistent data
+ break
+
+ case "ordnance":
+ childProperties.append( "ordnanceMods" ) // not in persistent data
+ break*/
+ }
+ }
+ else
+ {
+ switch ( propertyName )
+ {
+ /*case "setFile":
+ childProperties.append( "setFileMods" ) // not in persistent data
+ break
+
+ case "primary":
+ //childProperties.append( "primaryAttachment" ) // not in persistent data
+ childProperties.append( "primaryMod" )
+ break
+
+ case "special":
+ childProperties.append( "specialMods" ) // not in persistent data
+ break
+
+ case "ordnance":
+ childProperties.append( "ordnanceMods" ) // not in persistent data
+ break
+
+ case "antirodeo":
+ childProperties.append( "antirodeoMods" ) // not in persistent data
+ break*/
+ }
+ }
+
+ return childProperties
+}
+
+// There's no great way to know the dependency heirarchy of persistent loadout data
+string function GetParentLoadoutProperty( string loadoutType, string propertyName )
+{
+ Assert( loadoutType == "pilot" || loadoutType == "titan" )
+
+ string parentProperty
+
+ if ( loadoutType == "pilot" )
+ {
+ switch ( propertyName )
+ {
+ case "primaryAttachment":
+ case "primaryMod1":
+ case "primaryMod2":
+ case "primaryMod3":
+ case "primaryCamoIndex":
+ case "primarySkinIndex":
+ parentProperty = "primary"
+ break
+
+ case "secondaryMod1":
+ case "secondaryMod2":
+ case "secondaryMod3":
+ case "secondaryCamoIndex":
+ case "secondarySkinIndex":
+ parentProperty = "secondary"
+ break
+
+ case "weapon3Mod1":
+ case "weapon3Mod2":
+ case "weapon3Mod3":
+ case "weapon3CamoIndex":
+ case "weapon3SkinIndex":
+ parentProperty = "weapon3"
+ break
+
+ case "passive1":
+ case "passive2":
+ parentProperty = "special"
+ break
+
+ default:
+ Assert( 0, "Unknown loadout propertyName: " + propertyName )
+ }
+ }
+ else
+ {
+ switch ( propertyName )
+ {
+ case "passive1":
+ case "passive2":
+ case "passive3":
+ case "passive4":
+ case "passive5":
+ case "passive6":
+ parentProperty = "titanClass"
+ break
+
+ default:
+ Assert( 0, "Unknown loadout propertyName: " + propertyName )
+ }
+ }
+
+ return parentProperty
+}
+
+int function GetPersistentSpawnLoadoutIndex( entity player, string loadoutType )
+{
+ int loadoutIndex = player.GetPersistentVarAsInt( loadoutType + "SpawnLoadout.index" )
+ if ( loadoutType == "titan" && loadoutIndex >= NUM_PERSISTENT_TITAN_LOADOUTS )
+ loadoutIndex = 0
+
+ return loadoutIndex
+}
+
+#if SERVER
+void function SetPersistentSpawnLoadoutIndex( entity player, string loadoutType, int loadoutIndex )
+{
+ Assert( loadoutIndex >= 0 )
+ player.SetPersistentVar( loadoutType + "SpawnLoadout.index", loadoutIndex )
+}
+#endif // SERVER
+
+#if UI
+ void function SetEditLoadout( string loadoutType, int loadoutIndex )
+ {
+ uiGlobal.editingLoadoutType = loadoutType
+ uiGlobal.editingLoadoutIndex = loadoutIndex
+ }
+
+ void function ClearEditLoadout()
+ {
+ uiGlobal.editingLoadoutType = ""
+ uiGlobal.editingLoadoutIndex = -1
+ }
+
+ PilotLoadoutDef function GetPilotEditLoadout()
+ {
+ return GetCachedPilotLoadout( uiGlobal.editingLoadoutIndex )
+ }
+
+ TitanLoadoutDef function GetTitanEditLoadout()
+ {
+ return GetCachedTitanLoadout( uiGlobal.editingLoadoutIndex )
+ }
+
+ int function GetLoadoutIndexForTitanClass( string titanClass )
+ {
+ for ( int loadoutIndex = 0; loadoutIndex < NUM_PERSISTENT_TITAN_LOADOUTS; loadoutIndex++ )
+ {
+ TitanLoadoutDef loadout = GetCachedTitanLoadout( loadoutIndex )
+
+ if ( loadout.titanClass == titanClass )
+ return loadoutIndex
+ }
+
+ return -1
+ }
+
+ string function GetPilotLoadoutName( PilotLoadoutDef loadout )
+ {
+ if ( IsTokenLoadoutName( loadout.name ) )
+ return Localize( loadout.name )
+
+ return loadout.name
+ }
+
+ string function GetTitanLoadoutName( TitanLoadoutDef loadout )
+ {
+ if ( IsTokenLoadoutName( loadout.name ) )
+ return Localize( loadout.name )
+
+ return loadout.name
+ }
+
+ void function SetTextFromItemName( var element, string ref )
+ {
+ string text = ""
+
+ if ( ref != "" )
+ text = GetItemName( ref )
+
+ Hud_SetText( element, text )
+ }
+
+ void function SetTextFromItemDescription( var element, string ref )
+ {
+ string text = ""
+
+ if ( ref != "" )
+ text = GetItemDescription( ref )
+
+ Hud_SetText( element, text )
+ }
+
+ void function SetTextFromItemLongDescription( var element, string ref )
+ {
+ string text = ""
+
+ if ( ref != "" )
+ text = GetItemLongDescription( ref )
+
+ Hud_SetText( element, text )
+ }
+
+ void function SetImageFromItemImage( var element, string ref )
+ {
+ if ( ref != "" )
+ {
+ Hud_SetImage( element, GetItemImage( ref ) )
+ Hud_Show( element )
+ }
+ else
+ {
+ Hud_Hide( element )
+ }
+ }
+
+ void function SetTextFromSubItemClipSize( var element, string ref, string modRef )
+ {
+ Hud_SetText( element, "" )
+ if ( ref != "" && modRef != "" )
+ {
+ int clipDiff = GetSubItemClipSizeStat( ref, modRef )
+ if ( clipDiff == 0 )
+ return
+
+ if ( clipDiff > 0 )
+ {
+ Hud_SetColor( element, 141, 197, 84, 255 )
+ Hud_SetText( element, "#MOD_CLIP_AMMO_INCREASE", string( clipDiff ) )
+ }
+ else
+ {
+ Hud_SetColor( element, 211, 77, 61, 255 )
+ Hud_SetText( element, "#MOD_CLIP_AMMO_DECREASE", string( abs( clipDiff ) ) )
+ }
+ }
+ }
+
+ void function SetTextFromSubitemName( var element, string parentRef, string childRef, string defaultText = "" )
+ {
+ string text = defaultText
+
+ if ( parentRef != "" && childRef != "" && childRef != "none" )
+ text = GetSubitemName( parentRef, childRef )
+
+ Hud_SetText( element, text )
+ }
+
+ void function SetTextFromSubitemDescription( var element, string parentRef, string childRef, string defaultText = "" )
+ {
+ string text = defaultText
+
+ if ( parentRef != "" && childRef != "" && childRef != "none" )
+ text = GetSubitemDescription( parentRef, childRef )
+
+ Hud_SetText( element, text )
+ }
+
+ void function SetTextFromSubitemLongDescription( var element, string parentRef, string childRef, string defaultText = "" )
+ {
+ string text = defaultText
+
+ if ( parentRef != "" && childRef != "" && childRef != "none" )
+ text = GetSubitemLongDescription( parentRef, childRef )
+
+ Hud_SetText( element, text )
+ }
+
+ void function SetImageFromSubitemImage( var element, string parentRef, string childRef, asset defaultIcon = $"" )
+ {
+ if ( parentRef != "" && childRef != "" && childRef != "none" )
+ {
+ Hud_SetImage( element, GetSubitemImage( parentRef, childRef ) )
+ Hud_Show( element )
+ }
+ else
+ {
+ if ( defaultIcon != $"" )
+ {
+ Hud_SetImage( element, defaultIcon )
+ Hud_Show( element )
+ }
+ else
+ {
+ Hud_Hide( element )
+ }
+ }
+ }
+
+ void function SetTextFromSubitemUnlockReq( var element, string parentRef, string childRef, string defaultText = "" )
+ {
+ string text = defaultText
+
+ if ( parentRef != "" && childRef != "" && childRef != "none" )
+ text = GetItemUnlockReqText( childRef, parentRef )
+
+ Hud_SetText( element, text )
+ }
+#endif //UI
+
+#if UI || CLIENT
+ void function UpdateAllCachedPilotLoadouts()
+ {
+ int numLoadouts = shGlobal.cachedPilotLoadouts.len()
+
+ for ( int i = 0; i < numLoadouts; i++ )
+ UpdateCachedPilotLoadout( i )
+ }
+
+ void function UpdateAllCachedTitanLoadouts()
+ {
+ int numLoadouts = shGlobal.cachedTitanLoadouts.len()
+
+ for ( int i = 0; i < numLoadouts; i++ )
+ UpdateCachedTitanLoadout( i )
+ }
+
+ void function UpdateCachedPilotLoadout( int loadoutIndex )
+ {
+ entity player
+ #if UI
+ player = GetUIPlayer()
+ #elseif CLIENT
+ player = GetLocalClientPlayer()
+ #endif
+
+ if ( player == null )
+ return
+
+ PopulatePilotLoadoutFromPersistentData( player, shGlobal.cachedPilotLoadouts[ loadoutIndex ], loadoutIndex )
+ }
+
+ void function UpdateCachedTitanLoadout( int loadoutIndex )
+ {
+ entity player
+ #if UI
+ player = GetUIPlayer()
+ #elseif CLIENT
+ player = GetLocalClientPlayer()
+ #endif
+
+ if ( player == null )
+ return
+
+ PopulateTitanLoadoutFromPersistentData( player, shGlobal.cachedTitanLoadouts[ loadoutIndex ], loadoutIndex )
+ }
+
+ PilotLoadoutDef function GetCachedPilotLoadout( int loadoutIndex )
+ {
+ Assert( loadoutIndex >= 0 && loadoutIndex < shGlobal.cachedPilotLoadouts.len() )
+
+ return shGlobal.cachedPilotLoadouts[ loadoutIndex ]
+ }
+
+ TitanLoadoutDef function GetCachedTitanLoadout( int loadoutIndex )
+ {
+ Assert( loadoutIndex >= 0 && loadoutIndex < shGlobal.cachedTitanLoadouts.len() )
+
+ return shGlobal.cachedTitanLoadouts[ loadoutIndex ]
+ }
+
+ PilotLoadoutDef[ NUM_PERSISTENT_PILOT_LOADOUTS ] function GetAllCachedPilotLoadouts()
+ {
+ return shGlobal.cachedPilotLoadouts
+ }
+
+ TitanLoadoutDef[ NUM_PERSISTENT_TITAN_LOADOUTS ] function GetAllCachedTitanLoadouts()
+ {
+ return shGlobal.cachedTitanLoadouts
+ }
+
+ int function GetCachedTitanLoadoutCamoIndex( int loadoutIndex )
+ {
+ TitanLoadoutDef cachedTitanLoadout = GetCachedTitanLoadout( loadoutIndex )
+ return GetTitanCamoIndexFromLoadoutAndPrimeStatus( cachedTitanLoadout )
+ }
+
+ int function GetCachedTitanLoadoutSkinIndex( int loadoutIndex )
+ {
+ TitanLoadoutDef cachedTitanLoadout = GetCachedTitanLoadout( loadoutIndex )
+ return GetTitanSkinIndexFromLoadoutAndPrimeStatus( cachedTitanLoadout )
+ }
+
+ int function GetCachedTitanLoadoutDecalIndex( int loadoutIndex )
+ {
+ TitanLoadoutDef cachedTitanLoadout = GetCachedTitanLoadout( loadoutIndex )
+ return GetTitanDecalIndexFromLoadoutAndPrimeStatus( cachedTitanLoadout )
+ }
+
+ asset function GetCachedTitanLoadoutArmBadge( int loadoutIndex )
+ {
+ TitanLoadoutDef cachedTitanLoadout = GetCachedTitanLoadout( loadoutIndex )
+ return GetTitanArmBadgeFromLoadoutAndPrimeStatus( cachedTitanLoadout )
+ }
+
+ int function GetCachedTitanArmBadgeState( int loadoutIndex )
+ {
+ TitanLoadoutDef cachedTitanLoadout = GetCachedTitanLoadout( loadoutIndex )
+ return cachedTitanLoadout.showArmBadge
+ }
+
+#endif // UI || CLIENT
+
+#if UI
+ bool function IsTokenLoadoutName( string name )
+ {
+ if ( name.find( "#DEFAULT_PILOT_" ) != null || name.find( "#DEFAULT_TITAN_" ) != null )
+ return true
+
+ return false
+ }
+#endif // UI
+
+string function Loadouts_GetSetFileForRequestedClass( entity player )
+{
+ int loadoutIndex = GetPersistentSpawnLoadoutIndex( player, "pilot" )
+
+ PilotLoadoutDef loadout
+ PopulatePilotLoadoutFromPersistentData( player, loadout, loadoutIndex )
+
+ return loadout.setFile
+}
+
+#if SERVER
+ void function SetPersistentPilotLoadout( entity player, int loadoutIndex, PilotLoadoutDef loadout )
+ {
+ SetPersistentLoadoutValue( player, "pilot", loadoutIndex, "name", loadout.name )
+ SetPersistentLoadoutValue( player, "pilot", loadoutIndex, "suit", loadout.suit )
+ SetPersistentLoadoutValue( player, "pilot", loadoutIndex, "race", loadout.race )
+ SetPersistentLoadoutValue( player, "pilot", loadoutIndex, "primary", loadout.primary )
+ SetPersistentLoadoutValue( player, "pilot", loadoutIndex, "primaryAttachment", loadout.primaryAttachment )
+ SetPersistentLoadoutValue( player, "pilot", loadoutIndex, "primaryMod1", loadout.primaryMod1 )
+ SetPersistentLoadoutValue( player, "pilot", loadoutIndex, "primaryMod2", loadout.primaryMod2 )
+ SetPersistentLoadoutValue( player, "pilot", loadoutIndex, "primaryMod3", loadout.primaryMod3 )
+ SetPersistentLoadoutValue( player, "pilot", loadoutIndex, "secondary", loadout.secondary )
+ SetPersistentLoadoutValue( player, "pilot", loadoutIndex, "secondaryMod1", loadout.secondaryMod1 )
+ SetPersistentLoadoutValue( player, "pilot", loadoutIndex, "secondaryMod2", loadout.secondaryMod2 )
+ SetPersistentLoadoutValue( player, "pilot", loadoutIndex, "secondaryMod3", loadout.secondaryMod3 )
+ SetPersistentLoadoutValue( player, "pilot", loadoutIndex, "weapon3", loadout.weapon3 )
+ SetPersistentLoadoutValue( player, "pilot", loadoutIndex, "weapon3Mod1", loadout.weapon3Mod1 )
+ SetPersistentLoadoutValue( player, "pilot", loadoutIndex, "weapon3Mod2", loadout.weapon3Mod2 )
+ SetPersistentLoadoutValue( player, "pilot", loadoutIndex, "weapon3Mod3", loadout.weapon3Mod3 )
+ SetPersistentLoadoutValue( player, "pilot", loadoutIndex, "ordnance", loadout.ordnance )
+ SetPersistentLoadoutValue( player, "pilot", loadoutIndex, "passive1", loadout.passive1 )
+ SetPersistentLoadoutValue( player, "pilot", loadoutIndex, "passive2", loadout.passive2 )
+ }
+
+ void function SetPersistentTitanLoadout( entity player, int loadoutIndex, TitanLoadoutDef loadout )
+ {
+ SetPersistentLoadoutValue( player, "titan", loadoutIndex, "name", loadout.name )
+ SetPersistentLoadoutValue( player, "titan", loadoutIndex, "titanClass", GetTitanCharacterNameFromSetFile( loadout.setFile ) )
+ //SetPersistentLoadoutValue( player, "titan", loadoutIndex, "setFile", loadout.setFile )
+ //SetPersistentLoadoutValue( player, "titan", loadoutIndex, "primaryMod", loadout.primaryMod )
+ SetPersistentLoadoutValue( player, "titan", loadoutIndex, "special", loadout.special )
+ SetPersistentLoadoutValue( player, "titan", loadoutIndex, "antirodeo", loadout.antirodeo )
+ SetPersistentLoadoutValue( player, "titan", loadoutIndex, "passive1", loadout.passive1 )
+ SetPersistentLoadoutValue( player, "titan", loadoutIndex, "passive2", loadout.passive2 )
+ SetPersistentLoadoutValue( player, "titan", loadoutIndex, "passive3", loadout.passive3 )
+ SetPersistentLoadoutValue( player, "titan", loadoutIndex, "passive4", loadout.passive4 )
+ SetPersistentLoadoutValue( player, "titan", loadoutIndex, "passive5", loadout.passive5 )
+ SetPersistentLoadoutValue( player, "titan", loadoutIndex, "passive6", loadout.passive6 )
+ SetPersistentLoadoutValue( player, "titan", loadoutIndex, "titanExecution", loadout.titanExecution )
+ }
+
+ bool function PlayerIsInGracePeriod( entity player )
+ {
+ if ( svGlobal.isInPilotGracePeriod && !player.IsTitan() )
+ return true
+
+ if ( player.s.inGracePeriod )
+ return true
+
+ if ( player.p.usingLoadoutCrate )
+ return true
+
+ return false
+ }
+
+ bool function Loadouts_CanGivePilotLoadout( entity player )
+ {
+ if ( !IsAlive( player ) )
+ return false
+
+ if ( !PlayerIsInGracePeriod( player ) )
+ return false
+
+ // hack for bug 114632, 167264. Real fix would be to make dropship spawn script not end on anim reset from model change.
+ if ( player.GetParent() != null )
+ {
+ if ( HasCinematicFlag( player, CE_FLAG_INTRO ) )
+ return false
+
+ if ( HasCinematicFlag( player, CE_FLAG_CLASSIC_MP_SPAWNING ) )
+ return false
+
+ if ( HasCinematicFlag( player, CE_FLAG_WAVE_SPAWNING ) )
+ return false
+ }
+
+ if ( player.IsTitan() )
+ return false
+
+ return true
+ }
+
+ bool function Loadouts_CanGiveTitanLoadout( entity player )
+ {
+ // if ( GetPlayerBurnCardOnDeckIndex( player ) != null )
+ // return false
+
+ if ( HasCinematicFlag( player, CE_FLAG_INTRO ) )
+ return false
+
+ if ( HasCinematicFlag( player, CE_FLAG_CLASSIC_MP_SPAWNING ) )
+ return false
+
+ if ( !IsAlive( player ) )
+ return false
+
+ if ( !PlayerIsInGracePeriod( player ) )
+ return false
+
+ if ( player.isSpawningHotDroppingAsTitan )
+ return false
+
+ if ( !player.IsTitan() )
+ return false
+
+ // JFS: wargames round switch BS
+ if ( !IsValid( player.GetTitanSoul() ) )
+ return false
+
+ return true
+ }
+
+
+ string function Loadouts_GetSetFileForActiveClass( entity player )
+ {
+ int loadoutIndex = GetActivePilotLoadoutIndex( player )
+
+ PilotLoadoutDef loadout
+ PopulatePilotLoadoutFromPersistentData( player, loadout, loadoutIndex )
+
+ return loadout.setFile
+ }
+
+
+ string function Loadouts_GetPilotRaceForActiveClass( entity player )
+ {
+ int loadoutIndex = GetActivePilotLoadoutIndex( player )
+
+ PilotLoadoutDef loadout
+ PopulatePilotLoadoutFromPersistentData( player, loadout, loadoutIndex )
+
+ return loadout.race
+ }
+
+ bool function Loadouts_TryGivePilotLoadout( entity player )
+ {
+ if ( !Loadouts_CanGivePilotLoadout( player ) )
+ return false
+
+ PilotLoadoutDef loadout
+
+ int loadoutIndex = GetPersistentSpawnLoadoutIndex( player, "pilot" )
+
+ #if DEV
+ if ( player.IsBot() && !player.IsPlayback() && GetConVarString( "bot_pilot_settings" ) == "random" )
+ loadout = GetRandomPilotLoadout()
+ else
+ #endif
+ loadout = GetPilotLoadoutFromPersistentData( player, loadoutIndex )
+
+ UpdateDerivedPilotLoadoutData( loadout )
+
+ if ( player.IsBot() && !player.IsPlayback() )
+ OverrideBotPilotLoadout( loadout )
+
+ GivePilotLoadout( player, loadout )
+ SetActivePilotLoadout( player )
+ SetActivePilotLoadoutIndex( player, loadoutIndex )
+
+ //PROTO_DisplayPilotLoadouts( player, loadout )
+
+ return true
+ }
+
+ bool function Loadouts_TryGiveTitanLoadout( entity player )
+ {
+ if ( !Loadouts_CanGiveTitanLoadout( player ) )
+ return false
+
+ Assert( IsMultiplayer(), "Spawning as a Titan is not supported in SP currently" )
+
+ entity soul = player.GetTitanSoul()
+
+ TakeAllWeapons( player )
+ TakeAllPassives( player )
+
+ soul.passives = arrayofsize( GetNumPassives(), false ) //Clear out passives on soul
+
+ TitanLoadoutDef loadout
+ int loadoutIndex = GetPersistentSpawnLoadoutIndex( player, "titan" )
+
+ #if DEV
+ if ( player.IsBot() && !player.IsPlayback() )
+ {
+ string botTitanSettings = GetConVarString( "bot_titan_settings" )
+ loadout = GetRandomTitanLoadout( botTitanSettings )
+
+ array<string> legalLoadouts = GetAllowedTitanSetFiles()
+ if ( legalLoadouts.contains( botTitanSettings ) )
+ loadout.setFile = botTitanSettings //Overwrite just the setfile, mods etc will be random
+ }
+ else
+ #endif
+ loadout = GetTitanLoadoutFromPersistentData( player, loadoutIndex )
+
+ OverwriteLoadoutWithDefaultsForSetFile_ExceptSpecialAndAntiRodeo( loadout )
+
+ if ( player.IsBot() && !player.IsPlayback() )
+ OverrideBotTitanLoadout( loadout )
+
+ ApplyTitanLoadoutModifiers( player, loadout )
+
+ player.SetPlayerSettingsFromDataTable( { playerSetFile = loadout.setFile, playerSetFileMods = loadout.setFileMods } )
+ GiveTitanLoadout( player, loadout )
+ SetActiveTitanLoadoutIndex( player, loadoutIndex )
+ SetActiveTitanLoadout( player )
+ //PROTO_DisplayTitanLoadouts( player, player, loadout )
+
+ string settings = GetSoulPlayerSettings( soul )
+ var titanTint = Dev_GetPlayerSettingByKeyField_Global( settings, "titan_tint" )
+
+ if ( titanTint != null )
+ {
+ expect string( titanTint )
+ Highlight_SetEnemyHighlight( player, titanTint )
+ }
+ else
+ {
+ Highlight_ClearEnemyHighlight( player )
+ }
+
+ var title = GetPlayerSettingsFieldForClassName( settings, "printname" )
+ if ( title != null )
+ {
+ player.SetTitle( expect string( title ) )
+ }
+
+ return true
+ }
+
+ void function OverrideBotPilotLoadout( PilotLoadoutDef loadout )
+ {
+ string bot_force_pilot_primary = GetConVarString( "bot_force_pilot_primary" )
+ string bot_force_pilot_secondary = GetConVarString( "bot_force_pilot_secondary" )
+ //string bot_force_pilot_weapon3 = GetConVarString( "bot_force_pilot_weapon3" ) // TODO: Convar needs to be added in code
+ string bot_force_pilot_ordnance = GetConVarString( "bot_force_pilot_ordnance" )
+ string bot_force_pilot_ability = GetConVarString( "bot_force_pilot_ability" )
+
+ // Primary
+ if ( DevFindItemByName( eItemTypes.PILOT_PRIMARY, bot_force_pilot_primary ) )
+ {
+ loadout.primary = bot_force_pilot_primary
+ loadout.primaryAttachment = ""
+ loadout.primaryAttachments = []
+ loadout.primaryMod1 = ""
+ loadout.primaryMod2 = ""
+ loadout.primaryMod3 = ""
+ loadout.primaryMods = []
+ }
+
+ // Secondary
+ if ( DevFindItemByName( eItemTypes.PILOT_SECONDARY, bot_force_pilot_secondary ) )
+ {
+ loadout.secondary = bot_force_pilot_secondary
+ loadout.secondaryMod1 = ""
+ loadout.secondaryMod2 = ""
+ loadout.secondaryMod3 = ""
+ loadout.secondaryMods = []
+ }
+
+ // Weapon3
+ //if ( DevFindItemByName( eItemTypes.PILOT_SECONDARY, bot_force_pilot_weapon3 ) )
+ //{
+ // loadout.weapon3 = bot_force_pilot_weapon3
+ // loadout.weapon3Mod1 = ""
+ // loadout.weapon3Mod2 = ""
+ // loadout.weapon3Mod3 = ""
+ // loadout.weapon3Mods = []
+ //}
+
+ // Ordnance/Offhand
+ if ( DevFindItemByName( eItemTypes.PILOT_ORDNANCE, bot_force_pilot_ordnance ) )
+ {
+ loadout.ordnance = bot_force_pilot_ordnance
+ loadout.ordnanceMods = []
+ }
+
+ // Ability/Special
+ if ( DevFindItemByName( eItemTypes.PILOT_SPECIAL, bot_force_pilot_ability ) )
+ {
+ loadout.special = bot_force_pilot_ability
+ loadout.specialMods = []
+ }
+ }
+
+ void function OverrideBotTitanLoadout( TitanLoadoutDef loadout )
+ {
+ string bot_force_titan_primary = GetConVarString( "bot_force_titan_primary" )
+ string bot_force_titan_ordnance = GetConVarString( "bot_force_titan_ordnance" )
+ string bot_force_titan_ability = GetConVarString( "bot_force_titan_ability" )
+
+ // Primary
+ if ( DevFindItemByName( eItemTypes.TITAN_PRIMARY, bot_force_titan_primary ) )
+ {
+ loadout.primary = bot_force_titan_primary
+ loadout.primaryMod = ""
+ loadout.primaryMods = []
+ }
+
+ // Ordnance/Offhand
+ if ( DevFindItemByName( eItemTypes.TITAN_ORDNANCE, bot_force_titan_ordnance ) )
+ {
+ loadout.ordnance = bot_force_titan_ordnance
+ loadout.ordnanceMods = []
+ }
+
+ // Ability/Special
+ if ( DevFindItemByName( eItemTypes.TITAN_SPECIAL, bot_force_titan_ability ) )
+ {
+ loadout.special = bot_force_titan_ability
+ loadout.specialMods = []
+ }
+ }
+
+ TitanLoadoutDef function GetTitanSpawnLoadout( entity player )
+ {
+ int loadoutIndex = GetPersistentSpawnLoadoutIndex( player, "titan" )
+ TitanLoadoutDef loadout = GetTitanLoadoutFromPersistentData( player, loadoutIndex )
+ OverwriteLoadoutWithDefaultsForSetFile_ExceptSpecialAndAntiRodeo( loadout, player )
+
+ return loadout
+ }
+
+ // TODO: make loadout crate stuff not update your requested loadout
+ void function Loadouts_OnUsedLoadoutCrate( entity player )
+ {
+ int loadoutIndex = GetPersistentSpawnLoadoutIndex( player, "pilot" )
+
+ PilotLoadoutDef loadout = GetPilotLoadoutFromPersistentData( player, loadoutIndex )
+
+ GivePilotLoadout( player, loadout )
+ }
+
+ void function SetActivePilotLoadout( entity player )
+ {
+ PilotLoadoutDef loadout = GetPilotLoadoutFromPersistentData( player, GetPersistentSpawnLoadoutIndex( player, "pilot" ) )
+
+ player.SetPersistentVar( "activePilotLoadout.name", loadout.name )
+ player.SetPersistentVar( "activePilotLoadout.suit", loadout.suit )
+ player.SetPersistentVar( "activePilotLoadout.race", loadout.race )
+ player.SetPersistentVar( "activePilotLoadout.execution", loadout.execution )
+ player.SetPersistentVar( "activePilotLoadout.primary", loadout.primary )
+ player.SetPersistentVar( "activePilotLoadout.primaryAttachment", loadout.primaryAttachment )
+ player.SetPersistentVar( "activePilotLoadout.primaryMod1", loadout.primaryMod1 )
+ player.SetPersistentVar( "activePilotLoadout.primaryMod2", loadout.primaryMod2 )
+ player.SetPersistentVar( "activePilotLoadout.primaryMod3", loadout.primaryMod3 )
+ player.SetPersistentVar( "activePilotLoadout.secondary", loadout.secondary )
+ player.SetPersistentVar( "activePilotLoadout.secondaryMod1", loadout.secondaryMod1 )
+ player.SetPersistentVar( "activePilotLoadout.secondaryMod2", loadout.secondaryMod2 )
+ player.SetPersistentVar( "activePilotLoadout.secondaryMod3", loadout.secondaryMod3 )
+ player.SetPersistentVar( "activePilotLoadout.weapon3", loadout.weapon3 )
+ player.SetPersistentVar( "activePilotLoadout.weapon3Mod1", loadout.weapon3Mod1 )
+ player.SetPersistentVar( "activePilotLoadout.weapon3Mod2", loadout.weapon3Mod2 )
+ player.SetPersistentVar( "activePilotLoadout.weapon3Mod3", loadout.weapon3Mod3 )
+ player.SetPersistentVar( "activePilotLoadout.ordnance", loadout.ordnance )
+ player.SetPersistentVar( "activePilotLoadout.passive1", loadout.passive1 )
+ player.SetPersistentVar( "activePilotLoadout.passive2", loadout.passive2 )
+ player.SetPersistentVar( "activePilotLoadout.skinIndex", loadout.skinIndex )
+ player.SetPersistentVar( "activePilotLoadout.camoIndex", loadout.camoIndex )
+ player.SetPersistentVar( "activePilotLoadout.primarySkinIndex", loadout.primarySkinIndex )
+ player.SetPersistentVar( "activePilotLoadout.primaryCamoIndex", loadout.primaryCamoIndex )
+ player.SetPersistentVar( "activePilotLoadout.secondarySkinIndex", loadout.secondarySkinIndex )
+ player.SetPersistentVar( "activePilotLoadout.secondaryCamoIndex", loadout.secondaryCamoIndex )
+ player.SetPersistentVar( "activePilotLoadout.weapon3SkinIndex", loadout.weapon3SkinIndex )
+ player.SetPersistentVar( "activePilotLoadout.weapon3CamoIndex", loadout.weapon3CamoIndex )
+ }
+
+ void function SetActivePilotLoadoutIndex( entity player, int loadoutIndex )
+ {
+ player.p.activePilotLoadoutIndex = loadoutIndex
+ player.SetPlayerNetInt( "activePilotLoadoutIndex", loadoutIndex )
+ }
+
+ void function SetActiveTitanLoadout( entity player )
+ {
+ Assert( player.IsPlayer(), "Titan spawn loadout makes sense for players not NPCs")
+ TitanLoadoutDef loadout = GetTitanSpawnLoadout( player )
+
+ player.SetPersistentVar( "activeTitanLoadout.name", loadout.name )
+ player.SetPersistentVar( "activeTitanLoadout.titanClass", loadout.titanClass )
+ player.SetPersistentVar( "activeTitanLoadout.primaryMod", loadout.primaryMod )
+ player.SetPersistentVar( "activeTitanLoadout.special", loadout.special )
+ player.SetPersistentVar( "activeTitanLoadout.antirodeo", loadout.antirodeo )
+ player.SetPersistentVar( "activeTitanLoadout.passive1", loadout.passive1 )
+ player.SetPersistentVar( "activeTitanLoadout.passive2", loadout.passive2 )
+ player.SetPersistentVar( "activeTitanLoadout.passive3", loadout.passive3 )
+ player.SetPersistentVar( "activeTitanLoadout.passive4", loadout.passive4 )
+ player.SetPersistentVar( "activeTitanLoadout.passive5", loadout.passive5 )
+ player.SetPersistentVar( "activeTitanLoadout.passive6", loadout.passive6 )
+ player.SetPersistentVar( "activeTitanLoadout.skinIndex", loadout.skinIndex )
+ player.SetPersistentVar( "activeTitanLoadout.camoIndex", loadout.camoIndex )
+ player.SetPersistentVar( "activeTitanLoadout.decalIndex", loadout.decalIndex )
+ player.SetPersistentVar( "activeTitanLoadout.primarySkinIndex", loadout.primarySkinIndex )
+ player.SetPersistentVar( "activeTitanLoadout.primaryCamoIndex", loadout.primaryCamoIndex )
+ player.SetPersistentVar( "activeTitanLoadout.titanExecution", loadout.titanExecution )
+ player.SetPersistentVar( "activeTitanLoadout.isPrime", loadout.isPrime )
+ player.SetPersistentVar( "activeTitanLoadout.primeSkinIndex", loadout.primeSkinIndex )
+ player.SetPersistentVar( "activeTitanLoadout.primeCamoIndex", loadout.primeCamoIndex )
+ player.SetPersistentVar( "activeTitanLoadout.primeDecalIndex", loadout.primeDecalIndex )
+ player.SetPersistentVar( "activeTitanLoadout.showArmBadge", loadout.showArmBadge )
+ }
+
+ void function SetActiveTitanLoadoutIndex( entity player, int loadoutIndex )
+ {
+ //printt( ">>>>>>>>>>>>> SetActiveTitanLoadoutIndex() with index:", loadoutIndex )
+ player.p.activeTitanLoadoutIndex = loadoutIndex
+ player.SetPlayerNetInt( "activeTitanLoadoutIndex", loadoutIndex )
+ }
+
+ void function PROTO_DisplayTitanLoadouts( entity player, entity titan, TitanLoadoutDef loadout )
+ {
+ entity soul = titan.GetTitanSoul()
+ if ( soul.e.embarkCount > 0 )
+ return
+
+ if ( loadout.primary != "" )
+ PROTO_PlayLoadoutNotification( loadout.primary, player )
+ if ( loadout.ordnance != "" )
+ PROTO_PlayLoadoutNotification( loadout.ordnance, player )
+ if ( loadout.special != "" )
+ PROTO_PlayLoadoutNotification( loadout.special, player )
+ if ( loadout.passive1 != "" )
+ PROTO_PlayLoadoutNotification( loadout.passive1, player )
+ if ( loadout.passive2 != "" )
+ PROTO_PlayLoadoutNotification( loadout.passive2, player )
+ if ( loadout.passive3 != "" )
+ PROTO_PlayLoadoutNotification( loadout.passive3, player )
+ if ( loadout.passive4 != "" )
+ PROTO_PlayLoadoutNotification( loadout.passive4, player )
+ if ( loadout.passive5 != "" )
+ PROTO_PlayLoadoutNotification( loadout.passive5, player )
+ if ( loadout.passive6 != "" )
+ PROTO_PlayLoadoutNotification( loadout.passive6, player )
+ }
+
+ void function PROTO_DisplayPilotLoadouts( entity player, PilotLoadoutDef loadout )
+ {
+ if ( loadout.primary != "" )
+ PROTO_PlayLoadoutNotification( loadout.primary, player )
+ if ( loadout.secondary != "" )
+ PROTO_PlayLoadoutNotification( loadout.secondary, player )
+ if ( loadout.ordnance != "" )
+ PROTO_PlayLoadoutNotification( loadout.ordnance, player )
+ if ( loadout.special != "" )
+ PROTO_PlayLoadoutNotification( loadout.special, player )
+ if ( loadout.passive1 != "" )
+ PROTO_PlayLoadoutNotification( loadout.passive1, player )
+ if ( loadout.passive2 != "" )
+ PROTO_PlayLoadoutNotification( loadout.passive2, player )
+ }
+#endif //SERVER
+
+#if !UI
+ PilotLoadoutDef function GetActivePilotLoadout( entity player )
+ {
+ PilotLoadoutDef loadout
+ loadout.name = string( player.GetPersistentVar( "activePilotLoadout.name" ) )
+ loadout.suit = string( player.GetPersistentVar( "activePilotLoadout.suit" ) )
+ loadout.race = string( player.GetPersistentVar( "activePilotLoadout.race" ) )
+ loadout.execution = string( player.GetPersistentVar( "activePilotLoadout.execution" ) )
+ loadout.primary = string( player.GetPersistentVar( "activePilotLoadout.primary" ) )
+ loadout.primaryAttachment = string( player.GetPersistentVar( "activePilotLoadout.primaryAttachment" ) )
+ loadout.primaryMod1 = string( player.GetPersistentVar( "activePilotLoadout.primaryMod1" ) )
+ loadout.primaryMod2 = string( player.GetPersistentVar( "activePilotLoadout.primaryMod2" ) )
+ loadout.primaryMod3 = string( player.GetPersistentVar( "activePilotLoadout.primaryMod3" ) )
+ loadout.secondary = string( player.GetPersistentVar( "activePilotLoadout.secondary" ) )
+ loadout.secondaryMod1 = string( player.GetPersistentVar( "activePilotLoadout.secondaryMod1" ) )
+ loadout.secondaryMod2 = string( player.GetPersistentVar( "activePilotLoadout.secondaryMod2" ) )
+ loadout.secondaryMod3 = string( player.GetPersistentVar( "activePilotLoadout.secondaryMod3" ) )
+ loadout.weapon3 = string( player.GetPersistentVar( "activePilotLoadout.weapon3" ) )
+ loadout.weapon3Mod1 = string( player.GetPersistentVar( "activePilotLoadout.weapon3Mod1" ) )
+ loadout.weapon3Mod2 = string( player.GetPersistentVar( "activePilotLoadout.weapon3Mod2" ) )
+ loadout.weapon3Mod3 = string( player.GetPersistentVar( "activePilotLoadout.weapon3Mod3" ) )
+ loadout.ordnance = string( player.GetPersistentVar( "activePilotLoadout.ordnance" ) )
+ loadout.passive1 = string( player.GetPersistentVar( "activePilotLoadout.passive1" ) )
+ loadout.passive2 = string( player.GetPersistentVar( "activePilotLoadout.passive2" ) )
+ loadout.skinIndex = player.GetPersistentVarAsInt( "activePilotLoadout.skinIndex" )
+ loadout.camoIndex = player.GetPersistentVarAsInt( "activePilotLoadout.camoIndex" )
+ loadout.primarySkinIndex = player.GetPersistentVarAsInt( "activePilotLoadout.primarySkinIndex" )
+ loadout.primaryCamoIndex = player.GetPersistentVarAsInt( "activePilotLoadout.primaryCamoIndex" )
+ loadout.secondarySkinIndex = player.GetPersistentVarAsInt( "activePilotLoadout.secondarySkinIndex" )
+ loadout.secondaryCamoIndex = player.GetPersistentVarAsInt( "activePilotLoadout.secondaryCamoIndex" )
+ loadout.weapon3SkinIndex = player.GetPersistentVarAsInt( "activePilotLoadout.weapon3SkinIndex" )
+ loadout.weapon3CamoIndex = player.GetPersistentVarAsInt( "activePilotLoadout.weapon3CamoIndex" )
+
+ UpdateDerivedPilotLoadoutData( loadout )
+
+ return loadout
+ }
+
+ TitanLoadoutDef function GetActiveTitanLoadout( entity player )
+ {
+ TitanLoadoutDef loadout
+ loadout.name = string( player.GetPersistentVar( "activeTitanLoadout.name" ) )
+ loadout.titanClass = string( player.GetPersistentVar( "activeTitanLoadout.titanClass" ) )
+ loadout.primaryMod = string( player.GetPersistentVar( "activeTitanLoadout.primaryMod" ) )
+ loadout.special = string( player.GetPersistentVar( "activeTitanLoadout.special" ) )
+ loadout.antirodeo = string( player.GetPersistentVar( "activeTitanLoadout.antirodeo" ) )
+ loadout.passive1 = string( player.GetPersistentVar( "activeTitanLoadout.passive1" ) )
+ loadout.passive2 = string( player.GetPersistentVar( "activeTitanLoadout.passive2" ) )
+ loadout.passive3 = string( player.GetPersistentVar( "activeTitanLoadout.passive3" ) )
+ loadout.passive4 = string( player.GetPersistentVar( "activeTitanLoadout.passive4" ) )
+ loadout.passive5 = string( player.GetPersistentVar( "activeTitanLoadout.passive5" ) )
+ loadout.passive6 = string( player.GetPersistentVar( "activeTitanLoadout.passive6" ) )
+ loadout.titanExecution = string( player.GetPersistentVar( "activeTitanLoadout.titanExecution" ) )
+ loadout.skinIndex = player.GetPersistentVarAsInt( "activeTitanLoadout.skinIndex" )
+ loadout.camoIndex = player.GetPersistentVarAsInt( "activeTitanLoadout.camoIndex" )
+ loadout.decalIndex = player.GetPersistentVarAsInt( "activeTitanLoadout.decalIndex" )
+ loadout.primarySkinIndex = player.GetPersistentVarAsInt( "activeTitanLoadout.primarySkinIndex" )
+ loadout.primaryCamoIndex = player.GetPersistentVarAsInt( "activeTitanLoadout.primaryCamoIndex" )
+ loadout.isPrime = string( player.GetPersistentVar( "activeTitanLoadout.isPrime" ) )
+ loadout.primeSkinIndex = player.GetPersistentVarAsInt( "activeTitanLoadout.primeSkinIndex" )
+ loadout.primeCamoIndex = player.GetPersistentVarAsInt( "activeTitanLoadout.primeCamoIndex" )
+ loadout.primeDecalIndex = player.GetPersistentVarAsInt( "activeTitanLoadout.primeDecalIndex" )
+ loadout.showArmBadge = player.GetPersistentVarAsInt( "activeTitanLoadout.showArmBadge" )
+
+ UpdateDerivedTitanLoadoutData( loadout )
+ OverwriteLoadoutWithDefaultsForSetFile_ExceptSpecialAndAntiRodeo( loadout, player )
+
+ //int loadoutIndex = GetActiveTitanLoadoutIndex( player )
+
+ //TitanLoadoutDef loadout = GetTitanLoadoutFromPersistentData( player, loadoutIndex )
+ //OverwriteLoadoutWithDefaultsForSetFile_ExceptSpecialAndAntiRodeo( loadout )
+
+ return loadout
+ }
+
+ // When possible use GetActivePilotLoadout() instead
+ int function GetActivePilotLoadoutIndex( entity player )
+ {
+ #if SERVER
+ return player.p.activePilotLoadoutIndex
+ #else
+ return player.GetPlayerNetInt( "activePilotLoadoutIndex" )
+ #endif
+ }
+
+ int function GetActiveTitanLoadoutIndex( entity player )
+ {
+ //printt( "<<<<<<<<<<<<< GetActiveTitanLoadoutIndex() with index:", player.p.activeTitanLoadoutIndex )
+ #if SERVER
+ return player.p.activeTitanLoadoutIndex
+ #else
+ return player.GetPlayerNetInt( "activeTitanLoadoutIndex" )
+ #endif
+ }
+#endif
+
+// Added doOverrideCallback as a temporary measure to work around an issue the callback override added here is causing
+// This is called in PopulateDefaultPilotLoadouts() which sets the data GetDefaultPilotLoadout() returns
+// GetDefaultPilotLoadout() should never be returning overridden loadouts because it is used in many places to correct or reset persistent loadout data
+void function UpdateDerivedPilotLoadoutData( PilotLoadoutDef loadout, bool doOverrideCallback = true )
+{
+ loadout.setFile = GetSuitAndGenderBasedSetFile( loadout.suit, loadout.race )
+ loadout.special = GetSuitBasedTactical( loadout.suit )
+ loadout.primaryAttachments = [ loadout.primaryAttachment ]
+ loadout.primaryMods = [ loadout.primaryMod1, loadout.primaryMod2, loadout.primaryMod3 ]
+ loadout.secondaryMods = [ loadout.secondaryMod1, loadout.secondaryMod2, loadout.secondaryMod3 ]
+ loadout.weapon3Mods = [ loadout.weapon3Mod1, loadout.weapon3Mod2, loadout.weapon3Mod3 ]
+ loadout.setFileMods = GetSetFileModsForSettingType( "pilot", [ loadout.passive1, loadout.passive2 ] )
+
+ #if SERVER
+ if ( doOverrideCallback )
+ {
+ foreach ( callbackFunc in svGlobal.onUpdateDerivedPilotLoadoutCallbacks )
+ callbackFunc( loadout )
+ }
+ #endif
+}
+
+bool function TitanClassHasPrimeTitan( string titanClass )
+{
+ string nonPrimeSetFile = GetSetFileForTitanClassAndPrimeStatus( titanClass, false )
+ string primeSetFile = GetPrimeTitanSetFileFromNonPrimeSetFile( nonPrimeSetFile )
+
+ return primeSetFile != ""
+}
+
+bool function IsTitanLoadoutPrime( TitanLoadoutDef loadout )
+{
+ return loadout.isPrime == "titan_is_prime"
+}
+
+bool function IsTitanClassPrime( entity player, string titanClass )
+{
+ for ( int i = 0; i < NUM_PERSISTENT_TITAN_LOADOUTS; i++ )
+ {
+ TitanLoadoutDef loadout = GetTitanLoadoutFromPersistentData( player, i )
+ if ( loadout.titanClass == titanClass )
+ {
+ if ( loadout.isPrime == "titan_is_prime" )
+ return true
+ else
+ return false
+ }
+ }
+
+ unreachable
+}
+
+void function UpdateDerivedTitanLoadoutData( TitanLoadoutDef loadout )
+{
+ bool isTitanLoadoutPrime = IsTitanLoadoutPrime( loadout )
+ loadout.setFile = GetSetFileForTitanClassAndPrimeStatus( loadout.titanClass, IsTitanLoadoutPrime( loadout ) )
+ loadout.primeTitanRef = GetPrimeTitanRefForTitanClass( loadout.titanClass )
+}
+
+void function PrintPilotLoadoutIndex( entity player, int index )
+{
+ PrintPilotLoadout( GetPilotLoadoutFromPersistentData( player, index ) )
+}
+
+void function PrintTitanLoadoutIndex( entity player, int index )
+{
+ PrintTitanLoadout( GetTitanLoadoutFromPersistentData( player, index ) )
+}
+
+void function PrintPilotLoadouts( entity player )
+{
+ for ( int i = 0; i < NUM_PERSISTENT_PILOT_LOADOUTS; i++ )
+ PrintPilotLoadoutIndex( player, i )
+}
+
+void function PrintTitanLoadouts( entity player )
+{
+ for ( int i = 0; i < NUM_PERSISTENT_TITAN_LOADOUTS; i++ )
+ PrintTitanLoadoutIndex( player, i )
+}
+
+void function PrintPilotLoadout( PilotLoadoutDef loadout )
+{
+ printt( "PILOT LOADOUT:" )
+ printt( " PERSISTENT DATA:" )
+ printt( " name \"" + loadout.name + "\"" )
+ printt( " suit \"" + loadout.suit + "\"" )
+ printt( " race \"" + loadout.race + "\"" )
+ printt( " execution \"" + loadout.execution + "\"" )
+ printt( " primary \"" + loadout.primary + "\"" )
+ printt( " primaryAttachment \"" + loadout.primaryAttachment + "\"" )
+ printt( " primaryMod1 \"" + loadout.primaryMod1 + "\"" )
+ printt( " primaryMod2 \"" + loadout.primaryMod2 + "\"" )
+ printt( " primaryMod3 \"" + loadout.primaryMod3 + "\"" )
+ printt( " primarySkinIndex " + loadout.primarySkinIndex )
+ printt( " primaryCamoIndex " + loadout.primaryCamoIndex )
+ printt( " secondary \"" + loadout.secondary + "\"" )
+ printt( " secondaryMod1 \"" + loadout.secondaryMod1 + "\"" )
+ printt( " secondaryMod2 \"" + loadout.secondaryMod2 + "\"" )
+ printt( " secondaryMod3 \"" + loadout.secondaryMod3 + "\"" )
+ printt( " secondarySkinIndex " + loadout.secondarySkinIndex )
+ printt( " secondaryCamoIndex " + loadout.secondaryCamoIndex )
+ printt( " weapon3 \"" + loadout.weapon3 + "\"" )
+ printt( " weapon3Mod1 \"" + loadout.weapon3Mod1 + "\"" )
+ printt( " weapon3Mod2 \"" + loadout.weapon3Mod2 + "\"" )
+ printt( " weapon3Mod3 \"" + loadout.weapon3Mod3 + "\"" )
+ printt( " weapon3SkinIndex " + loadout.weapon3SkinIndex )
+ printt( " weapon3CamoIndex " + loadout.weapon3CamoIndex )
+ printt( " ordnance \"" + loadout.ordnance + "\"" )
+ printt( " special \"" + loadout.special + "\"" )
+ printt( " passive1 \"" + loadout.passive1 + "\"" )
+ printt( " passive2 \"" + loadout.passive2 + "\"" )
+ printt( " skinIndex " + loadout.skinIndex )
+ printt( " camoIndex " + loadout.camoIndex )
+ printt( " DERIVED DATA:" )
+ printt( " setFile \"" + loadout.setFile + "\"" )
+ print( " setFileMods " )
+ PrintStringArray( loadout.setFileMods )
+ printt( " melee \"" + loadout.melee + "\"" )
+ print( " meleeMods " )
+ PrintStringArray( loadout.meleeMods )
+ print( " primaryAttachments " )
+ PrintStringArray( loadout.primaryAttachments )
+ print( " primaryMods " )
+ PrintStringArray( loadout.primaryMods )
+ print( " secondaryMods " )
+ PrintStringArray( loadout.secondaryMods )
+ print( " weapon3Mods " )
+ PrintStringArray( loadout.weapon3Mods )
+ print( " specialMods " )
+ PrintStringArray( loadout.specialMods )
+ print( " ordnanceMods " )
+ PrintStringArray( loadout.ordnanceMods )
+}
+
+void function PrintTitanLoadout( TitanLoadoutDef loadout )
+{
+ printt( "TITAN LOADOUT:" )
+ printt( " PERSISTENT DATA:" )
+ printt( " name \"" + loadout.name + "\"" )
+ printt( " titanClass \"" + loadout.titanClass + "\"" )
+ printt( " setFile \"" + loadout.setFile + "\"" )
+ printt( " primeTitanRef \"" + loadout.primeTitanRef + "\"" )
+ printt( " primaryMod \"" + loadout.primaryMod + "\"" )
+ printt( " special \"" + loadout.special + "\"" )
+ printt( " antirodeo \"" + loadout.antirodeo + "\"" )
+ printt( " passive1 \"" + loadout.passive1 + "\"" )
+ printt( " passive2 \"" + loadout.passive2 + "\"" )
+ printt( " passive3 \"" + loadout.passive3 + "\"" )
+ printt( " passive4 \"" + loadout.passive4 + "\"" )
+ printt( " passive5 \"" + loadout.passive5 + "\"" )
+ printt( " passive6 \"" + loadout.passive6 + "\"" )
+ printt( " voice \"" + loadout.voice + "\"" )
+ printt( " skinIndex " + loadout.skinIndex )
+ printt( " camoIndex " + loadout.camoIndex )
+ printt( " decalIndex " + loadout.decalIndex )
+ printt( " primarySkinIndex " + loadout.primarySkinIndex )
+ printt( " primaryCamoIndex " + loadout.primaryCamoIndex )
+ printt( " isPrime \"" + loadout.isPrime + "\"" )
+ printt( " primeSkinIndex " + loadout.primeSkinIndex )
+ printt( " primeCamoIndex " + loadout.primeCamoIndex )
+ printt( " primeDecalIndex " + loadout.primeDecalIndex )
+ printt( " DERIVED DATA:" )
+ print( " setFileMods " )
+ PrintStringArray( loadout.setFileMods )
+ printt( " melee \"" + loadout.melee + "\"" )
+ printt( " coreAbility \"" + loadout.coreAbility + "\"" )
+ printt( " primary \"" + loadout.primary + "\"" )
+ printt( " primaryAttachment \"" + loadout.primaryAttachment + "\"" )
+ print( " primaryMods " )
+ PrintStringArray( loadout.primaryMods )
+ printt( " ordnance \"" + loadout.ordnance + "\"" )
+ print( " ordnanceMods " )
+ PrintStringArray( loadout.ordnanceMods )
+ print( " specialMods " )
+ PrintStringArray( loadout.specialMods )
+ print( " antirodeoMods " )
+ PrintStringArray( loadout.antirodeoMods )
+}
+
+void function PrintStringArray( array<string> stringArray )
+{
+ if ( stringArray.len() == 0 )
+ {
+ print( "[]" )
+ }
+ else
+ {
+ for ( int i = 0; i < stringArray.len(); i++ )
+ {
+ if ( i == 0 )
+ print( "[ " )
+
+ print( "\"" + stringArray[i] + "\"" )
+
+ if ( i+1 < stringArray.len() )
+ print( ", " )
+ else
+ print( " ]" )
+ }
+ }
+
+ print( "\n" )
+}
+
+string function GetSkinPropertyName( string camoPropertyName )
+{
+ string skinPropertyName
+
+ switch ( camoPropertyName )
+ {
+ case "camoIndex":
+ skinPropertyName = "skinIndex"
+ break
+
+ case "primeCamoIndex":
+ skinPropertyName = "primeSkinIndex"
+ break
+
+ case "primaryCamoIndex":
+ skinPropertyName = "primarySkinIndex"
+ break
+
+ case "secondaryCamoIndex":
+ skinPropertyName = "secondarySkinIndex"
+ break
+
+ case "weapon3CamoIndex":
+ skinPropertyName = "weapon3SkinIndex"
+ break
+
+ default:
+ Assert( false, "Unknown camoPropertyName: " + camoPropertyName )
+ break
+ }
+
+ return skinPropertyName
+}
+
+int function GetSkinIndexForCamo( string modelType, string camoPropertyName, int camoIndex )
+{
+ Assert( modelType == "pilot" || modelType == "titan" )
+ Assert( camoPropertyName == "camoIndex" || camoPropertyName == "primeCamoIndex" || camoPropertyName == "primaryCamoIndex" || camoPropertyName == "secondaryCamoIndex" || camoPropertyName == "weapon3CamoIndex" )
+
+ int skinIndex = SKIN_INDEX_BASE
+
+ if ( camoIndex > 0 )
+ {
+ if ( camoPropertyName == "camoIndex" || camoPropertyName == "primeCamoIndex" )
+ {
+ if ( modelType == "pilot" )
+ skinIndex = PILOT_SKIN_INDEX_CAMO
+ else
+ skinIndex = TITAN_SKIN_INDEX_CAMO
+ }
+ else if ( camoPropertyName == "primaryCamoIndex" || camoPropertyName == "secondaryCamoIndex" || camoPropertyName == "weapon3CamoIndex" )
+ {
+ skinIndex = WEAPON_SKIN_INDEX_CAMO
+ }
+ }
+
+ return skinIndex
+}
+
+#if SERVER
+void function UpdateProScreen( entity player, entity weapon )
+{
+ int proScreenKills = WeaponGetProScreenKills( player, weapon.GetWeaponClassName() )
+ int previousProScreenKills = WeaponGetPreviousProScreenKills( player, weapon.GetWeaponClassName() )
+ weapon.SetProScreenIntValForIndex( PRO_SCREEN_INT_LIFETIME_KILLS, proScreenKills )
+ weapon.SetProScreenIntValForIndex( PRO_SCREEN_INT_MATCH_KILLS, proScreenKills - previousProScreenKills )
+}
+#endif
+
+bool function IsValidPilotLoadoutIndex( int loadoutIndex )
+{
+ if ( loadoutIndex < 0 )
+ return false
+
+ if( loadoutIndex >= NUM_PERSISTENT_PILOT_LOADOUTS )
+ return false
+
+ return true
+}
+
+bool function IsValidTitanLoadoutIndex( int loadoutIndex )
+{
+ if ( loadoutIndex < 0 )
+ return false
+
+ if( loadoutIndex >= NUM_PERSISTENT_TITAN_LOADOUTS )
+ return false
+
+ return true
+}
+
+bool function HasPrimeToMatchExecutionType( entity player, int itemType )
+{
+ if ( DevEverythingUnlocked() )
+ return true
+
+ switch( itemType )
+ {
+ case eItemTypes.TITAN_RONIN_EXECUTION:
+ return !IsItemLocked( player, "ronin_prime" )
+ case eItemTypes.TITAN_NORTHSTAR_EXECUTION:
+ return !IsItemLocked( player, "northstar_prime" )
+ case eItemTypes.TITAN_ION_EXECUTION:
+ return !IsItemLocked( player, "ion_prime" )
+ case eItemTypes.TITAN_TONE_EXECUTION:
+ return !IsItemLocked( player, "tone_prime" )
+ case eItemTypes.TITAN_SCORCH_EXECUTION:
+ return !IsItemLocked( player, "scorch_prime" )
+ case eItemTypes.TITAN_LEGION_EXECUTION:
+ return !IsItemLocked( player, "legion_prime" )
+ case eItemTypes.TITAN_VANGUARD_EXECUTION:
+ return false
+
+ default:
+ unreachable
+ }
+ unreachable
+}
+
+bool function IsTitanLoadoutAvailable( entity player, string titanClass )
+{
+ int titanClassLockState = player.GetPersistentVarAsInt( "titanClassLockState[" + titanClass + "]" )
+ return (titanClassLockState == TITAN_CLASS_LOCK_STATE_AVAILABLE || titanClassLockState == TITAN_CLASS_LOCK_STATE_LEVELRECOMMENDED)
+}
+
+int function GetTitanLoadAvailableState( entity player, string titanClass )
+{
+ return player.GetPersistentVarAsInt( "titanClassLockState[" + titanClass + "]" )
+}
+
+#if UI || CLIENT
+void function SwapSecondaryAndWeapon3LoadoutData( entity player, int loadoutIndex )
+{
+ // Keep CLIENT matching UI
+ #if UI
+ RunClientScript( "SwapSecondaryAndWeapon3LoadoutData", player, loadoutIndex )
+ #endif // UI
+
+ string loadoutType = "pilot"
+
+ PilotLoadoutDef loadout
+ loadout.secondary = shGlobal.cachedPilotLoadouts[ loadoutIndex ].secondary
+ loadout.secondaryMod1 = shGlobal.cachedPilotLoadouts[ loadoutIndex ].secondaryMod1
+ loadout.secondaryMod2 = shGlobal.cachedPilotLoadouts[ loadoutIndex ].secondaryMod2
+ loadout.secondaryMod3 = shGlobal.cachedPilotLoadouts[ loadoutIndex ].secondaryMod3
+ loadout.secondarySkinIndex = shGlobal.cachedPilotLoadouts[ loadoutIndex ].secondarySkinIndex
+ loadout.secondaryCamoIndex = shGlobal.cachedPilotLoadouts[ loadoutIndex ].secondaryCamoIndex
+
+ loadout.weapon3 = shGlobal.cachedPilotLoadouts[ loadoutIndex ].weapon3
+ loadout.weapon3Mod1 = shGlobal.cachedPilotLoadouts[ loadoutIndex ].weapon3Mod1
+ loadout.weapon3Mod2 = shGlobal.cachedPilotLoadouts[ loadoutIndex ].weapon3Mod2
+ loadout.weapon3Mod3 = shGlobal.cachedPilotLoadouts[ loadoutIndex ].weapon3Mod3
+ loadout.weapon3SkinIndex = shGlobal.cachedPilotLoadouts[ loadoutIndex ].weapon3SkinIndex
+ loadout.weapon3CamoIndex = shGlobal.cachedPilotLoadouts[ loadoutIndex ].weapon3CamoIndex
+
+ SetPilotLoadoutValue( shGlobal.cachedPilotLoadouts[ loadoutIndex ], "secondary", loadout.weapon3 )
+ SetPilotLoadoutValue( shGlobal.cachedPilotLoadouts[ loadoutIndex ], "secondaryMod1", loadout.weapon3Mod1 )
+ SetPilotLoadoutValue( shGlobal.cachedPilotLoadouts[ loadoutIndex ], "secondaryMod2", loadout.weapon3Mod2 )
+ SetPilotLoadoutValue( shGlobal.cachedPilotLoadouts[ loadoutIndex ], "secondaryMod3", loadout.weapon3Mod3 )
+ SetPilotLoadoutValue( shGlobal.cachedPilotLoadouts[ loadoutIndex ], "secondarySkinIndex", string( loadout.weapon3SkinIndex ) )
+ SetPilotLoadoutValue( shGlobal.cachedPilotLoadouts[ loadoutIndex ], "secondaryCamoIndex", string( loadout.weapon3CamoIndex ) )
+
+ SetPilotLoadoutValue( shGlobal.cachedPilotLoadouts[ loadoutIndex ], "weapon3", loadout.secondary )
+ SetPilotLoadoutValue( shGlobal.cachedPilotLoadouts[ loadoutIndex ], "weapon3Mod1", loadout.secondaryMod1 )
+ SetPilotLoadoutValue( shGlobal.cachedPilotLoadouts[ loadoutIndex ], "weapon3Mod2", loadout.secondaryMod2 )
+ SetPilotLoadoutValue( shGlobal.cachedPilotLoadouts[ loadoutIndex ], "weapon3Mod3", loadout.secondaryMod3 )
+ SetPilotLoadoutValue( shGlobal.cachedPilotLoadouts[ loadoutIndex ], "weapon3SkinIndex", string( loadout.secondarySkinIndex ) )
+ SetPilotLoadoutValue( shGlobal.cachedPilotLoadouts[ loadoutIndex ], "weapon3CamoIndex", string( loadout.secondaryCamoIndex ) )
+
+ #if UI
+ ClientCommand( "SwapSecondaryAndWeapon3PersistentLoadoutData " + loadoutIndex )
+ #endif // UI
+}
+#endif // UI || CLIENT
|