using System; using System.Collections.Generic; using KernelPanic.Core; using KernelPanic.Meta; using UnityEngine; using UnityEngine.TextCore.Text; using UnityEngine.UIElements; #if UNITY_EDITOR using UnityEditor; #endif namespace KernelPanic.UI { /// /// Binds the main menu terminal UI document and routes command activation between panels. /// [RequireComponent(typeof(UIDocument))] public sealed class MainMenuController : MonoBehaviour { private const string HiddenClassName = "hidden"; private const string SelectedClassName = "selected"; private const string CursorOnClassName = "cursor-on"; private const float BootIntroSeconds = 1.5f; private static bool bootIntroPlayed; [SerializeField] private FontAsset monospaceFont; // TODO: Assign a real monospace FontAsset when typography assets exist. [SerializeField] private string motdBody = "unstable userspace detected; keep a rollback shell open."; [SerializeField] private FeaturedUnitPanel featuredUnitPanel = new(); private readonly List commandEntries = new(); private UIDocument document; private VisualElement root; private VisualElement shellRoot; private VisualElement bootIntroPanel; private VisualElement mainMenuPanel; private VisualElement collectionPanel; private VisualElement gachaPanel; private VisualElement settingsPanel; private VisualElement eventBanner; private VisualElement backgroundLogLayer; private Label appIdLabel; private Label entropyLabel; private Label pullTokensLabel; private Label titleCursorLabel; private Label promptCursorLabel; private Label bootIntroLogLabel; private Label motdBodyLabel; private VisualElement motdBlock; private Button collectionBackButton; private Button gachaBackButton; private Button settingsBackButton; private EntropyWallet wallet; private GachaService gachaService; private PlayerCollection playerCollection; private BackgroundLogRingBuffer backgroundLog; private IEventBannerSource eventBannerSource; private int selectedCommandIndex; private float bootIntroElapsed; private int bootIntroCharacterCount; private bool cursorVisible; private bool suppressNextClick; private string bootIntroCopy; private IVisualElementScheduledItem blinkSchedule; private IVisualElementScheduledItem bootIntroSchedule; public void Initialize(EntropyWallet initializedWallet) { wallet = initializedWallet ?? new EntropyWallet(); RefreshCurrencyReadouts(); } private void Awake() { document = GetComponent(); gachaService = new GachaService(); playerCollection = new PlayerCollection(); // TODO: Replace with persistent player-collection service composition. Initialize(new EntropyWallet()); // TODO: Replace with persistent wallet service composition. BindElements(); BindCommandEntries(); RegisterCommandEntryCallbacks(); featuredUnitPanel.Bind(root); ApplyOptionalFont(); } private void OnEnable() { RegisterCallbacks(); root.Focus(); shellRoot.EnableInClassList("reduced-motion", UIPreferences.ReducedMotion); RefreshStaticText(); RefreshCurrencyReadouts(); RefreshEventBanner(); featuredUnitPanel.Refresh(playerCollection.OwnedUnits); SelectCommand(0); ShowMainMenu(); StartAmbientSchedules(); PlayBootIntroIfNeeded(); } private void OnDisable() { UnregisterCallbacks(); blinkSchedule?.Pause(); backgroundLog?.Stop(); bootIntroSchedule?.Pause(); } private void BindElements() { root = document.rootVisualElement; shellRoot = root.Q("ShellRoot"); bootIntroPanel = root.Q("BootIntroPanel"); mainMenuPanel = root.Q("MainMenuPanel"); collectionPanel = root.Q("CollectionPanel"); gachaPanel = root.Q("GachaPanel"); settingsPanel = root.Q("SettingsPanel"); eventBanner = root.Q("EventBanner"); motdBlock = root.Q("MotdBlock"); backgroundLogLayer = root.Q("BackgroundLogLayer"); backgroundLog = new BackgroundLogRingBuffer(backgroundLogLayer, BootLogCopy.Lines); appIdLabel = root.Q