using MenuChanger; using MenuChanger.Extensions; using MenuChanger.MenuElements; using MenuChanger.MenuPanels; using Modding; using System; using UnityEngine; namespace Archipelago.HollowKnight.MC { internal class ArchipelagoModeMenuConstructor : ModeMenuConstructor { private MenuPage modeConfigPage; private readonly static Type _settingsType = typeof(ConnectionDetails); private readonly static Font _perpetua = CanvasUtil.GetFont("Perpetua"); public override void OnEnterMainMenu(MenuPage modeMenu) { modeConfigPage = new MenuPage("Archipelago Settings", modeMenu); ConnectionDetails settings = ArchipelagoMod.Instance.GS.MenuConnectionDetails; EntryField urlField = CreateUrlField(modeConfigPage, settings); NumericEntryField portField = CreatePortField(modeConfigPage, settings); EntryField nameField = CreateSlotNameField(modeConfigPage, settings); EntryField passwordField = CreatePasswordField(modeConfigPage, settings); MenuLabel errorLabel = new(modeConfigPage, ""); BigButton startButton = new(modeConfigPage, "Start", "May stall after clicking"); startButton.AddSetResumeKeyEvent("Archipelago"); startButton.OnClick += () => StartOrResumeGame(true, errorLabel); modeConfigPage.AfterHide += () => errorLabel.Text.text = ""; IMenuElement[] elements = [ urlField, portField, nameField, passwordField, startButton, errorLabel ]; VerticalItemPanel vip = new(modeConfigPage, SpaceParameters.TOP_CENTER_UNDER_TITLE, 100, false, elements); modeConfigPage.AddToNavigationControl(vip); AttachResumePage(); } private void AttachResumePage() { MenuPage resumePage = new("Archipelago Resume"); MenuLabel slotName = new(resumePage, ""); EntryField urlField = CreateUrlField(resumePage, null); NumericEntryField portField = CreatePortField(resumePage, null); EntryField passwordField = CreatePasswordField(resumePage, null); SmallButton resumeButton = new(resumePage, "Resume"); MenuLabel errorLabel = new(resumePage, ""); void RebindSettings() { ConnectionDetails settings = ArchipelagoMod.Instance.LS.ConnectionDetails; if (settings != null) { slotName.Text.text = $"Slot Name: {settings.SlotName}"; urlField.Bind(settings, _settingsType.GetProperty(nameof(ConnectionDetails.ServerUrl))); portField.Bind(settings, _settingsType.GetProperty(nameof(ConnectionDetails.ServerPort))); passwordField.Bind(settings, _settingsType.GetProperty(nameof(ConnectionDetails.ServerPassword))); } else { slotName.Text.text = "Incompatible save file"; errorLabel.Text.text = "To resume, recreate your save or downgrade to an older client version."; } } resumeButton.OnClick += () => StartOrResumeGame(false, errorLabel); resumePage.BeforeShow += RebindSettings; resumePage.AfterHide += () => errorLabel.Text.text = ""; IMenuElement[] elements = [ slotName, urlField, portField, passwordField, resumeButton, errorLabel ]; VerticalItemPanel vip = new(resumePage, SpaceParameters.TOP_CENTER_UNDER_TITLE, 100, true, elements); resumePage.AddToNavigationControl(vip); ResumeMenu.AddResumePage("Archipelago", resumePage); } private static EntryField CreateUrlField(MenuPage apPage, ConnectionDetails settings) { EntryField urlField = new(apPage, "Server URL: "); urlField.InputField.characterLimit = 500; RectTransform urlRect = urlField.InputField.gameObject.transform.Find("Text").GetComponent(); urlRect.sizeDelta = new Vector2(1500f, 63.2f); urlField.InputField.textComponent.font = _perpetua; if (settings != null) { urlField.Bind(settings, _settingsType.GetProperty(nameof(ConnectionDetails.ServerUrl))); } return urlField; } private static NumericEntryField CreatePortField(MenuPage apPage, ConnectionDetails settings) { NumericEntryField portField = new(apPage, "Server Port: "); portField.SetClamp(0, 65535); portField.InputField.textComponent.font = _perpetua; if (settings != null) { portField.Bind(settings, _settingsType.GetProperty(nameof(ConnectionDetails.ServerPort))); } return portField; } private static EntryField CreateSlotNameField(MenuPage apPage, ConnectionDetails settings) { EntryField nameField = new(apPage, "Slot Name: "); nameField.InputField.characterLimit = 500; nameField.InputField.textComponent.font = _perpetua; RectTransform nameRect = nameField.InputField.gameObject.transform.Find("Text").GetComponent(); nameRect.sizeDelta = new Vector2(1500f, 63.2f); if (settings != null) { nameField.Bind(settings, _settingsType.GetProperty(nameof(ConnectionDetails.SlotName))); } return nameField; } private static EntryField CreatePasswordField(MenuPage apPage, ConnectionDetails settings) { EntryField passwordField = new(apPage, "Password: "); passwordField.InputField.characterLimit = 500; passwordField.InputField.textComponent.font = _perpetua; RectTransform passwordRect = passwordField.InputField.gameObject.transform.Find("Text").GetComponent(); passwordRect.sizeDelta = new Vector2(1500f, 63.2f); if (settings != null) { passwordField.Bind(settings, _settingsType.GetProperty(nameof(ConnectionDetails.ServerPassword))); } return passwordField; } private static void StartOrResumeGame(bool newGame, MenuLabel errorLabel) { ArchipelagoMod.Instance.ArchipelagoEnabled = true; // Cloning some settings onto others depending on what is taking precedence. // If it's a save slot we're resuming (newGame == false) then we want the slot settings to overwrite the global ones. if (newGame) { ArchipelagoMod.Instance.LS = new APLocalSettings() { ConnectionDetails = ArchipelagoMod.Instance.GS.MenuConnectionDetails with { }, }; } else if (ArchipelagoMod.Instance.LS.ConnectionDetails != null) { ArchipelagoMod.Instance.GS.MenuConnectionDetails = ArchipelagoMod.Instance.LS.ConnectionDetails with { }; } try { ArchipelagoMod.Instance.StartOrResumeGame(newGame); MenuChangerMod.HideAllMenuPages(); if (newGame) { UIManager.instance.StartNewGame(); } else { UIManager.instance.ContinueGame(); GameManager.instance.ContinueGame(); } } catch (LoginValidationException ex) { ArchipelagoMod.Instance.DisconnectArchipelago(); errorLabel.Text.text = ex.Message; } catch (Exception ex) { errorLabel.Text.text = "An unknown error occurred when attempting to connect."; ArchipelagoMod.Instance.LogError(ex); ArchipelagoMod.Instance.DisconnectArchipelago(); } } public override void OnExitMainMenu() { modeConfigPage = null; } public override bool TryGetModeButton(MenuPage modeMenu, out BigButton button) { button = new BigButton(modeMenu, ArchipelagoMod.Instance.spriteManager.GetSprite("IconColorBig"), "Archipelago"); button.AddHideAndShowEvent(modeMenu, modeConfigPage); return true; } } }