using System; using Luneraria.Core; using UnityEngine; using UnityEngine.UIElements; namespace Luneraria.UI { /// /// Start screen of Champions of Luneraria (port of the network part of lune_menu.gd): /// offline (local host with bot seats), host a direct connection, or join one. The menu is /// the start screen of the game; the HUD's "Menü" returns here. Peer 1 is the host; free /// seats stay bots. The host is authoritative. /// public sealed class LuneMainMenu : VisualElement { public event Action StartOffline; public event Action HostRequested; public event Action JoinRequested; public event Action QuitRequested; public string Bund = "wurzelbund"; public string Lane = "mid"; readonly TextField _name; readonly TextField _address; readonly TextField _port; readonly Label _status; LuneButton _laneTop; LuneButton _laneMid; LuneButton _laneBot; public LuneMainMenu() { style.position = Position.Absolute; style.left = 0f; style.top = 0f; style.right = 0f; style.bottom = 0f; style.justifyContent = Justify.Center; style.alignItems = Align.Center; style.backgroundColor = new Color(.02f, .03f, .05f, .92f); var panel = new LunePanel(LunePanels.Menu); panel.style.minWidth = 560f; panel.style.maxWidth = 640f; Add(panel); VisualElement column = LuneUi.Box(true, 14f, panel); column.style.alignItems = Align.Center; LuneUi.Label("CHAMPIONS", "TitleLabel", column).style.fontSize = 56f; LuneUi.Label("OF LUNERARIA", "TitleLabel", column).style.fontSize = 40f; LuneUi.Label("DREI LANES · 3 GEGEN 3 · AUTOBATTLER", "EyebrowLabel", column); LuneUi.Wrap(LuneUi.Label("Team Blau (Südwest) mit zwei Bot-Verbündeten gegen drei Bots in Team Rot. Kein Rundenlimit: Es gewinnt, wer den gegnerische Ancient zerstört. Für sechs Menschen hosten oder beitreten; freie Plätze bleiben Bots. Der Host ist autoritativ.", "MutedLabel", column)); LuneUi.Label("DEIN BUND", "SectionLabel", column); VisualElement bundRow = LuneUi.Box(false, 10f, column); new LuneButton("Glutpakt", LuneButtons.HudToggle, bundRow).Clicked += () => SetBund("glutpakt"); new LuneButton("Wurzelbund", LuneButtons.HudToggle, bundRow).Clicked += () => SetBund("wurzelbund"); LuneUi.Label("DEINE LANE", "SectionLabel", column); VisualElement laneRow = LuneUi.Box(false, 10f, column); _laneTop = new LuneButton("Top", LuneButtons.HudToggle, laneRow); _laneMid = new LuneButton("Mid", LuneButtons.HudToggle, laneRow); _laneBot = new LuneButton("Bottom", LuneButtons.HudToggle, laneRow); _laneTop.ToggleMode = true; _laneMid.ToggleMode = true; _laneBot.ToggleMode = true; _laneTop.Clicked += () => SetLane("top"); _laneMid.Clicked += () => SetLane("mid"); _laneBot.Clicked += () => SetLane("bot"); SetLane("mid"); LuneUi.Label("NETZWERK · DIREKTVERBINDUNG", "SectionLabel", column); _name = Field(column, "Anzeigename", "Spieler"); VisualElement netRow = LuneUi.Box(false, 10f, column); _address = Field(netRow, "Host-Adresse", "127.0.0.1"); _address.style.flexGrow = 1f; _port = Field(netRow, "Port", LuneNet.DefaultPort.ToString()); _port.style.width = 100f; VisualElement actions = LuneUi.Box(false, 12f, column); new LuneButton("ALLEIN SPIELEN", LuneButtons.Primary, actions).Clicked += StartAlone; new LuneButton("HOSTEN", LuneButtons.Secondary, actions).Clicked += StartHost; new LuneButton("BEITRETEN", LuneButtons.Secondary, actions).Clicked += StartJoin; new LuneButton("BEENDEN", LuneButtons.Secondary, actions).Clicked += () => QuitRequested?.Invoke(); _status = LuneUi.Label("", "MutedLabel", column); _status.style.unityTextAlign = TextAnchor.MiddleCenter; } static TextField Field(VisualElement parent, string label, string value) { VisualElement box = LuneUi.Box(true, 4f, parent); box.style.flexGrow = 1f; LuneUi.Label(label, "EyebrowLabel", box); var field = new TextField { value = value }; field.style.flexGrow = 1f; field.style.minHeight = 32f; field.style.fontSize = 16f; box.Add(field); return field; } void SetBund(string bund) { Bund = bund; } void SetLane(string lane) { Lane = lane; _laneTop.On = lane == "top"; _laneMid.On = lane == "mid"; _laneBot.On = lane == "bot"; } /// Own seat on team Blau: slots 0–2 map to top, mid, bot. public MatchOptions OfflineOptions() { int seat = Array.IndexOf(LuneMap.Lanes, Lane); if (seat < 0) seat = 1; var options = new MatchOptions { Seed = Environment.TickCount & 0xffff, HumanSlot = seat }; for (int slot = 0; slot < 6; slot++) options.Bunds.Add(slot == seat ? Bund : DataCatalog.Factions[(slot + options.Seed) % DataCatalog.Factions.Length]); return options; } void StartAlone() { StartOffline?.Invoke(OfflineOptions()); } void StartHost() { int port = ParsePort(); HostRequested?.Invoke(port, CleanName()); } void StartJoin() { int port = ParsePort(); JoinRequested?.Invoke(_address.value.Trim(), port, CleanName()); } int ParsePort() { int port; return int.TryParse(_port.value.Trim(), out port) && port > 0 ? port : LuneNet.DefaultPort; } string CleanName() { string name = (_name.value ?? "").Trim(); if (name.Length == 0) name = "Spieler"; return name.Length > 24 ? name.Substring(0, 24) : name; } public void Notify(string message, bool error = false) { _status.text = message ?? ""; _status.style.color = error ? LuneUiTheme.Hex("ff8a70") : LuneUiTheme.Muted; } public void Enter() { LuneUi.Show(this, true); Notify(""); } public void Leave() { LuneUi.Show(this, false); } } }