diff --git a/Aetheria.State.Verify/Program.cs b/Aetheria.State.Verify/Program.cs index 9900473fa..fcef89f18 100644 --- a/Aetheria.State.Verify/Program.cs +++ b/Aetheria.State.Verify/Program.cs @@ -3798,6 +3798,8 @@ static void RequireSectorMapZoneDetailsUseEveSurface(string root) static void RequireRuntimeMenuTabsUseEveSurface(string root) { var menuPanelPath = Path.Combine(root, "Assets", "Scripts", "UI", "Menu", "MenuPanel.cs"); + var localMenuPath = Path.Combine(root, "Assets", "Scripts", "UI", "Menu", "LocalMenu.cs"); + var actionGameManagerPath = Path.Combine(root, "Assets", "Scripts", "Gameplay", "ActionGameManager.cs"); var menuTabsSurfaceBuilderPath = Path.Combine( root, "Packages", @@ -3808,6 +3810,14 @@ static void RequireRuntimeMenuTabsUseEveSurface(string root) { throw new InvalidOperationException("Cannot verify runtime menu tab shell; MenuPanel.cs is missing."); } + if (!File.Exists(localMenuPath)) + { + throw new InvalidOperationException("Cannot verify runtime local-menu shell; LocalMenu.cs is missing."); + } + if (!File.Exists(actionGameManagerPath)) + { + throw new InvalidOperationException("Cannot verify runtime menu tab shell; ActionGameManager.cs is missing."); + } if (!File.Exists(menuTabsSurfaceBuilderPath)) { throw new InvalidOperationException("Cannot verify runtime menu tab shell; AetheriaRuntimeMenuTabsSurfaceBuilder.cs is missing."); @@ -3820,6 +3830,8 @@ static void RequireRuntimeMenuTabsUseEveSurface(string root) } var source = File.ReadAllText(menuPanelPath); + var localMenu = File.ReadAllText(localMenuPath); + var actionGameManager = File.ReadAllText(actionGameManagerPath); var surfaceHostPath = Path.Combine(root, "Packages", "org.gamecult.aetheria.eve-runtime", "Runtime", "AetheriaEveUnitySurfaceHost.cs"); if (!File.Exists(surfaceHostPath)) { @@ -3839,6 +3851,8 @@ static void RequireRuntimeMenuTabsUseEveSurface(string root) "AetheriaRuntimeMenuTabsSurfaceBuilder.Build(ProjectTabSurfaceState())", "ProjectTabSurfaceState(", "ResolveVisibleTabs(", + "GameManager.IsObservedDocked", + "GameManager.TryGetObservedDockedLocalStory(out _)", "GetTabLabel(", "AetheriaRuntimeMenuTabsSurfaceCommands.TryRead(request, out var command)", "AetheriaRuntimeMenuTabCommandKind.SelectTab", @@ -3902,6 +3916,31 @@ static void RequireRuntimeMenuTabsUseEveSurface(string root) string.Join(", ", hits)); } + var requiredDockedStoryObserverSymbols = new[] + { + "public bool IsObservedDocked => DockedEntity != null && TryGetDaemonEntitySnapshot(DockedEntity, out _);", + "public bool TryGetObservedDockedLocalStory(out LocationStory story)", + "TryGetObservedDockedLocalStory(out _currentLocation)" + }; + var dockedStoryObserverCorpus = actionGameManager + "\n" + localMenu; + var missingDockedStoryObserverSymbols = requiredDockedStoryObserverSymbols + .Where(symbol => !dockedStoryObserverCorpus.Contains(symbol, StringComparison.Ordinal)) + .ToArray(); + if (missingDockedStoryObserverSymbols.Length > 0) + { + throw new InvalidOperationException( + "Runtime menu/local story visibility no longer flows through daemon-observed dock state: " + + string.Join(", ", missingDockedStoryObserverSymbols)); + } + + if (source.Contains("GameManager.DockedEntity != null", StringComparison.Ordinal) || + source.Contains("GameManager.DockedEntity as OrbitalEntity", StringComparison.Ordinal) || + localMenu.Contains("ActionGameManager.Instance.DockedEntity", StringComparison.Ordinal)) + { + throw new InvalidOperationException( + "Runtime menu/local UI must not inspect DockedEntity directly for dock/story visibility; ask the gameplay observer."); + } + var requiredBuilderSymbols = new[] { "public static class AetheriaRuntimeMenuTabsSurfaceBuilder", diff --git a/Assets/Scripts/Gameplay/ActionGameManager.cs b/Assets/Scripts/Gameplay/ActionGameManager.cs index 906ccebbf..7d5b7d947 100644 --- a/Assets/Scripts/Gameplay/ActionGameManager.cs +++ b/Assets/Scripts/Gameplay/ActionGameManager.cs @@ -494,6 +494,18 @@ public class ActionGameManager : MonoBehaviour set => _currentEntity = value; } + public bool IsObservedDocked => DockedEntity != null && TryGetDaemonEntitySnapshot(DockedEntity, out _); + + public bool TryGetObservedDockedLocalStory(out LocationStory story) + { + story = null; + if (!IsObservedDocked || DockedEntity is not OrbitalEntity { Story: { } dockedStory }) + return false; + + story = dockedStory; + return true; + } + public ItemManager ItemManager { get; private set; } public Zone Zone { get; private set; } public List LoadoutTemplates { get; } = new List(); diff --git a/Assets/Scripts/UI/Menu/LocalMenu.cs b/Assets/Scripts/UI/Menu/LocalMenu.cs index 996ec3863..5d8a1ffe5 100644 --- a/Assets/Scripts/UI/Menu/LocalMenu.cs +++ b/Assets/Scripts/UI/Menu/LocalMenu.cs @@ -22,12 +22,13 @@ public class LocalMenu : MonoBehaviour private void OnEnable() { - if (ActionGameManager.Instance.DockedEntity is OrbitalEntity orbital) + if (ActionGameManager.Instance == null || + !ActionGameManager.Instance.TryGetObservedDockedLocalStory(out _currentLocation)) { - _currentLocation = orbital.Story; - _activeStory = _currentLocation.Story; + return; } - else return; + + _activeStory = _currentLocation.Story; Continue(); } diff --git a/Assets/Scripts/UI/Menu/MenuPanel.cs b/Assets/Scripts/UI/Menu/MenuPanel.cs index 91d22e143..818ab549d 100644 --- a/Assets/Scripts/UI/Menu/MenuPanel.cs +++ b/Assets/Scripts/UI/Menu/MenuPanel.cs @@ -159,8 +159,8 @@ public class MenuPanel : MonoBehaviour private MenuTabBinding[] ResolveVisibleTabs() { return _tabs.Values - .Where(tabBinding => !tabBinding.RequireDock || GameManager.DockedEntity != null) - .Where(tabBinding => tabBinding.Tab != MenuTab.Local || (GameManager.DockedEntity as OrbitalEntity)?.Story != null) + .Where(tabBinding => !tabBinding.RequireDock || GameManager.IsObservedDocked) + .Where(tabBinding => tabBinding.Tab != MenuTab.Local || GameManager.TryGetObservedDockedLocalStory(out _)) .OrderBy(tabBinding => (int)tabBinding.Tab) .ToArray(); }