using ProjectM.Simulation; using Unity.Entities; using UnityEngine; using UnityEngine.UIElements; namespace ProjectM.Client { /// /// Drives the front-end main menu (UI Toolkit). Lives on a GameObject (with a UIDocument) in /// MainMenu.unity — build index 0. On it ENSURES a default "menu" world exists (the /// bootstrap creates one on first boot, but on return-from-game World.DisposeAllWorlds left none and /// Initialize does not re-run), ensures the EventSystem, and assigns the shared PanelSettings; on /// enable it builds the menu. Single/Host/Join hand off to . /// [RequireComponent(typeof(UIDocument))] public class MainMenuController : MonoBehaviour { UIDocument _doc; VisualElement _mainPanel; VisualElement _settingsPanel; TextField _ipField; Label _classLabel; void Awake() { EnsureMenuWorld(); MenuUi.EnsureEventSystem(); _doc = GetComponent(); if (_doc.panelSettings == null) _doc.panelSettings = MenuUi.LoadPanelSettings(); // The menu owns the cursor. UnityEngine.Cursor.lockState = CursorLockMode.None; UnityEngine.Cursor.visible = true; } void OnEnable() { if (_doc == null) _doc = GetComponent(); var root = _doc.rootVisualElement; if (root == null) return; root.Clear(); BuildMain(root); } static void EnsureMenuWorld() { var w = World.DefaultGameObjectInjectionWorld; if (w == null || !w.IsCreated) DefaultWorldInitialization.Initialize("MenuWorld", false); } void BuildMain(VisualElement root) { _mainPanel = MenuUi.FullScreenRoot(true); var card = MenuUi.Card("PROJECT M"); card.Add(MenuUi.Caption("Frontier colony — co-op")); // Slice 2: class picker -> sets WorldLauncher.SelectedClass for the next session (Warrior melee / Ranger ranged). _classLabel = new Label(ClassName(WorldLauncher.SelectedClass)); _classLabel.style.unityTextAlign = TextAnchor.MiddleCenter; _classLabel.style.marginTop = 6; _classLabel.style.marginBottom = 2; card.Add(_classLabel); var classRow = new VisualElement(); classRow.style.flexDirection = FlexDirection.Row; classRow.style.justifyContent = Justify.Center; classRow.Add(MenuUi.Button("Warrior", () => SelectClass((byte)CharacterId.Warrior))); classRow.Add(MenuUi.Button("Ranger", () => SelectClass((byte)CharacterId.Ranger))); card.Add(classRow); card.Add(MenuUi.Button("Single Player", () => Launch(SessionMode.Single, false))); if (SaveService.HasSave()) card.Add(MenuUi.Button("Continue", () => Launch(SessionMode.Single, true))); card.Add(MenuUi.Button("Host Co-op (LAN)", () => Launch(SessionMode.Host, SaveService.HasSave()))); _ipField = new TextField("Join IP") { value = "127.0.0.1" }; _ipField.style.marginTop = 8; card.Add(_ipField); card.Add(MenuUi.Button("Join", () => Launch(SessionMode.Join, false))); card.Add(MenuUi.Button("Settings", ShowSettings)); card.Add(MenuUi.Button("Quit", Quit)); _mainPanel.Add(card); root.Add(_mainPanel); } void Launch(SessionMode mode, bool loadSave) { string ip = _ipField != null ? _ipField.value : "127.0.0.1"; WorldLauncher.StartSession(mode, ip, loadSave); } void SelectClass(byte classId) { WorldLauncher.SelectedClass = classId; if (_classLabel != null) _classLabel.text = ClassName(classId); } static string ClassName(byte classId) => classId == (byte)CharacterId.Ranger ? "CLASS: Ranger (ranged)" : "CLASS: Warrior (melee)"; void ShowSettings() { _mainPanel.style.display = DisplayStyle.None; _settingsPanel = SettingsScreen.Build(HideSettings); _doc.rootVisualElement.Add(_settingsPanel); } void HideSettings() { if (_settingsPanel != null) { _settingsPanel.RemoveFromHierarchy(); _settingsPanel = null; } _mainPanel.style.display = DisplayStyle.Flex; } static void Quit() { #if UNITY_EDITOR UnityEditor.EditorApplication.isPlaying = false; #else Application.Quit(); #endif } } }