Aetheria/Assets/Scripts/UI/MainMenu.cs

492 lines
16 KiB
C#
Raw Normal View History

using System;
2026-06-21 09:37:54 +01:00
using GameCult.Aetheria.EveRuntime;
2026-06-21 09:54:15 +01:00
using GameCult.Aetheria.State.Verse;
using GameCult.Eve.Surface;
2026-06-28 22:36:46 +01:00
using GameCult.Mesh;
using TMPro;
using UniRx;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
using UnityEngine.UIElements;
public class MainMenu : MonoBehaviour
{
public VolumeCloudRenderer CloudRenderer;
public GameSettings Settings;
public ConfirmationDialog Dialog;
public bool InGame;
2026-06-17 04:57:56 +01:00
private UIDocument _menuSurfaceDocument;
2026-06-25 18:09:33 +01:00
private string _clientStatePath;
2026-06-28 22:36:46 +01:00
private CultMeshReactiveDocument<AetheriaRuntimeSectorMapDocument> _sectorMap;
private CultMeshReactiveDocument<AetheriaRuntimePlayerSettingsDocument> _playerSettings;
private CultMeshReactiveDocument<AetheriaRuntimeVerseHostSettingsDocument> _verseHostSettings;
2026-06-25 18:09:33 +01:00
private Func<bool> _canOpenRuntimeInputScreen;
private Action _openRuntimeInputScreen;
2026-06-21 09:37:54 +01:00
private readonly AetheriaEveUnitySurfaceChrome _menuSurfaceChrome = new AetheriaEveUnitySurfaceChrome
{
RootAlignItems = Align.Center,
RootJustifyContent = Justify.Center,
RootPaddingLeft = 24f,
RootPaddingRight = 24f,
RootPaddingTop = 24f,
RootPaddingBottom = 24f,
RootBackgroundColor = new Color(0f, 0f, 0f, 0.6f),
Width = 560f,
MinWidth = 0f,
MaxWidth = 560f,
PaddingLeft = 20f,
PaddingRight = 20f,
PaddingTop = 20f,
PaddingBottom = 20f,
BorderRadius = 0f,
BorderWidth = 0f,
BackgroundColor = new Color(0.08f, 0.1f, 0.14f, 0.96f)
};
void Start()
{
ShowMain();
}
2026-06-17 04:57:56 +01:00
private void ShowMain()
{
var stateBoot = CurrentStateBoot();
var sectorMap = ResolveSectorMap(stateBoot);
var verseHost = ResolveVerseHostSettings(stateBoot);
var playerSettings = ResolvePlayerSettings(stateBoot);
RenderMenuSurface(
2026-06-21 09:37:54 +01:00
AetheriaRuntimeMainMenuSurfaceBuilder.BuildRoot(
2026-06-21 21:52:46 +01:00
AetheriaRuntimeMainMenuSurfaceBuilder.ProjectRoot(
2026-06-21 09:37:54 +01:00
stateBoot,
2026-06-25 18:09:33 +01:00
sectorMap,
2026-06-21 09:37:54 +01:00
verseHost,
2026-06-21 21:52:46 +01:00
playerSettings,
2026-06-21 09:37:54 +01:00
CanOpenRuntimeInputScreen(),
2026-06-21 21:52:46 +01:00
InGame,
DateTime.UtcNow.ToString("O"))),
HandleMainSurfaceCommand);
}
2026-06-17 04:57:56 +01:00
private void HandleMainSurfaceCommand(EveSurfaceCommandRequest request)
{
2026-06-21 09:37:54 +01:00
if (!AetheriaRuntimeMainMenuSurfaceCommands.TryRead(request, out var command))
2026-06-17 04:57:56 +01:00
{
2026-06-21 09:37:54 +01:00
Debug.LogWarning("Unknown main menu command.");
return;
}
switch (command.Kind)
{
case AetheriaRuntimeMainMenuCommandKind.ContinueRun:
if (ResolveSectorMap(CurrentStateBoot()) == null)
2026-06-17 04:57:56 +01:00
{
2026-06-25 18:09:33 +01:00
Debug.LogWarning("Main-menu Continue requested without a typed Aetheria sector-map projection.");
2026-06-17 04:57:56 +01:00
ShowMain();
return;
}
2026-06-21 09:37:54 +01:00
ContinueGame();
2026-06-17 04:57:56 +01:00
return;
2026-06-21 09:37:54 +01:00
case AetheriaRuntimeMainMenuCommandKind.NewGame:
2026-06-17 04:57:56 +01:00
StartNewGame();
return;
2026-06-21 09:37:54 +01:00
case AetheriaRuntimeMainMenuCommandKind.ShowSettings:
2026-06-17 04:57:56 +01:00
ShowSettings();
return;
2026-06-21 09:37:54 +01:00
case AetheriaRuntimeMainMenuCommandKind.Quit:
2026-06-17 04:57:56 +01:00
Application.Quit();
return;
default:
2026-06-21 09:37:54 +01:00
Debug.LogWarning($"Unhandled main menu command kind: {command.Kind}");
2026-06-17 04:57:56 +01:00
return;
}
}
2026-06-17 04:57:56 +01:00
private void StartNewGame()
{
2026-06-21 09:37:54 +01:00
if (!TryStartDaemonObservedGame("Starting Daemon Run"))
{
ShowMain();
}
}
private bool TryStartDaemonObservedGame(string title)
{
var stateBoot = CurrentStateBoot();
var sectorMap = ResolveSectorMap(stateBoot);
2026-06-25 18:09:33 +01:00
if (sectorMap == null)
2026-06-21 09:37:54 +01:00
{
2026-06-25 18:09:33 +01:00
Debug.LogWarning($"Cannot start Aetheria observer scene without a typed Aetheria sector-map projection in {stateBoot.StateFilePath}.");
2026-06-21 09:37:54 +01:00
return false;
}
var generatorState = "Loading daemon sector";
2026-06-17 04:57:56 +01:00
Action<string> setState = s => generatorState = s;
HideMenuSurface();
2026-06-17 04:57:56 +01:00
Dialog.Clear();
2026-06-21 09:37:54 +01:00
Dialog.Title.text = title;
2026-06-17 04:57:56 +01:00
Dialog.AddProperty(() => generatorState);
Dialog.Show();
2026-06-25 18:09:33 +01:00
setState($"Observing sector-map frame {sectorMap.FrameId}");
2026-06-21 09:37:54 +01:00
SceneManager.LoadScene("ARPG");
return true;
}
private static AetheriaRuntimeStateBootReport CurrentStateBoot()
{
2026-06-25 18:09:33 +01:00
return AetheriaRuntimeStateBoot.Inspect(AetheriaUnityRuntimePaths.GameDataDirectory);
}
private AetheriaRuntimeSectorMapDocument ResolveSectorMap(AetheriaRuntimeStateBootReport stateBoot)
2026-06-16 06:09:46 +01:00
{
if (!stateBoot.SupportsLocalStateFileRead || !stateBoot.StateFileExists)
2026-06-16 06:09:46 +01:00
{
return null;
}
2026-06-21 09:37:54 +01:00
try
{
_sectorMap ??= ResolveClient(stateBoot)
2026-06-28 13:13:07 +01:00
.State
2026-06-28 23:50:28 +01:00
.Reactive<AetheriaRuntimeSectorMapDocument>();
return _sectorMap?.Current;
}
catch (Exception ex)
{
Debug.LogError($"Failed to bind typed Aetheria sector-map state for the main menu: {ex}");
return null;
}
2026-06-16 06:09:46 +01:00
}
private AetheriaRuntimePlayerSettingsDocument ResolvePlayerSettings(AetheriaRuntimeStateBootReport stateBoot)
2026-06-21 21:52:46 +01:00
{
if (!stateBoot.SupportsLocalStateFileRead || !stateBoot.StateFileExists)
return null;
try
{
_playerSettings ??= ResolveClient(stateBoot)
2026-06-28 13:13:07 +01:00
.State
2026-06-28 23:50:28 +01:00
.Reactive<AetheriaRuntimePlayerSettingsDocument>();
return _playerSettings?.Current;
2026-06-21 21:52:46 +01:00
}
catch (Exception ex)
{
Debug.LogError($"Failed to bind typed Aetheria player settings for the main menu: {ex}");
2026-06-21 21:52:46 +01:00
return null;
}
}
private AetheriaRuntimeVerseHostSettingsDocument ResolveVerseHostSettings(AetheriaRuntimeStateBootReport stateBoot)
{
if (!stateBoot.SupportsLocalStateFileRead || !stateBoot.StateFileExists)
return null;
try
{
_verseHostSettings ??= ResolveClient(stateBoot)
2026-06-28 13:13:07 +01:00
.State
2026-06-28 23:50:28 +01:00
.Reactive<AetheriaRuntimeVerseHostSettingsDocument>();
return _verseHostSettings?.Current;
}
catch (Exception ex)
{
Debug.LogError($"Failed to bind typed Aetheria Verse host settings for the main menu: {ex}");
return null;
}
}
2026-06-21 09:37:54 +01:00
private void ContinueGame()
2026-06-16 06:09:46 +01:00
{
2026-06-21 09:37:54 +01:00
if (!TryStartDaemonObservedGame("Continuing Daemon Run"))
2026-06-16 06:09:46 +01:00
{
2026-06-21 09:37:54 +01:00
ShowMain();
}
2026-06-16 06:09:46 +01:00
}
private void ShowSettings()
{
2026-06-21 09:37:54 +01:00
RenderMenuSurface(
AetheriaRuntimeMainMenuSurfaceBuilder.BuildSettings(DateTime.UtcNow.ToString("O")),
HandleSettingsSurfaceCommand);
}
private void ShowInputSettings()
{
2026-06-21 21:52:46 +01:00
var stateBoot = CurrentStateBoot();
2026-06-21 09:37:54 +01:00
RenderMenuSurface(
AetheriaRuntimeMainMenuSurfaceBuilder.BuildInputSettings(
2026-06-21 21:52:46 +01:00
AetheriaRuntimeMainMenuSurfaceBuilder.ProjectRoot(
stateBoot,
ResolvePlayerSettings(stateBoot),
2026-06-21 09:37:54 +01:00
CanOpenRuntimeInputScreen(),
2026-06-21 21:52:46 +01:00
InGame,
DateTime.UtcNow.ToString("O"))),
2026-06-21 09:37:54 +01:00
HandleInputSettingsSurfaceCommand);
}
private void ShowPlayerSettingsSurface()
{
RenderMenuSurface(
2026-06-21 21:52:46 +01:00
AetheriaRuntimeMainMenuSurfaceBuilder.BuildPlayerSettingsShell(
AetheriaRuntimeMainMenuSurfaceBuilder.ProjectPlayerSettings(
ResolvePlayerSettings(CurrentStateBoot()),
2026-06-21 21:52:46 +01:00
DateTime.UtcNow.ToString("O"))),
HandlePlayerSettingsSurfaceCommand);
}
private void ShowVerseSettingsSurface()
{
2026-06-21 21:52:46 +01:00
var stateBoot = CurrentStateBoot();
RenderMenuSurface(
2026-06-21 21:52:46 +01:00
AetheriaRuntimeMainMenuSurfaceBuilder.BuildVerseSettingsShell(
AetheriaRuntimeMainMenuSurfaceBuilder.ProjectVerseSettings(
stateBoot,
ResolveVerseHostSettings(stateBoot),
2026-06-21 21:52:46 +01:00
DateTime.UtcNow.ToString("O"))),
HandleVerseSettingsSurfaceCommand);
}
private void RenderMenuSurface(
2026-06-21 09:37:54 +01:00
AetheriaRuntimeSurfaceDocument document,
Action<EveSurfaceCommandRequest> commandHandler)
{
2026-06-21 09:37:54 +01:00
_menuSurfaceDocument = AetheriaEveUnitySurfaceHost.RenderRuntime(
transform,
_menuSurfaceDocument,
"Aetheria Menu Surface",
document,
commandHandler,
_menuSurfaceChrome);
}
private void HandleSettingsSurfaceCommand(EveSurfaceCommandRequest request)
{
2026-06-21 09:37:54 +01:00
if (!AetheriaRuntimeMainMenuSurfaceCommands.TryRead(request, out var command))
{
2026-06-21 09:37:54 +01:00
Debug.LogWarning("Unknown settings menu command.");
return;
}
switch (command.Kind)
{
case AetheriaRuntimeMainMenuCommandKind.ShowPlayerSettings:
ShowPlayerSettingsSurface();
return;
2026-06-21 09:37:54 +01:00
case AetheriaRuntimeMainMenuCommandKind.ShowVerseSettings:
ShowVerseSettingsSurface();
return;
2026-06-21 09:37:54 +01:00
case AetheriaRuntimeMainMenuCommandKind.ShowInputSettings:
ShowInputSettings();
return;
2026-06-21 09:37:54 +01:00
case AetheriaRuntimeMainMenuCommandKind.BackToMain:
ShowMain();
return;
default:
2026-06-21 09:37:54 +01:00
Debug.LogWarning($"Unhandled settings menu command kind: {command.Kind}");
return;
}
}
private void HandleInputSettingsSurfaceCommand(EveSurfaceCommandRequest request)
{
2026-06-21 09:37:54 +01:00
if (!AetheriaRuntimeMainMenuSurfaceCommands.TryRead(request, out var command))
{
2026-06-21 09:37:54 +01:00
Debug.LogWarning("Unknown input settings command.");
return;
}
2026-06-21 09:37:54 +01:00
switch (command.Kind)
{
2026-06-21 09:37:54 +01:00
case AetheriaRuntimeMainMenuCommandKind.BackToSettings:
ShowSettings();
return;
case AetheriaRuntimeMainMenuCommandKind.OpenRuntimeInputScreen:
if (!TryOpenRuntimeInputScreen())
{
ShowInputSettings();
}
2026-06-21 09:37:54 +01:00
return;
default:
Debug.LogWarning($"Unhandled input settings command kind: {command.Kind}");
return;
}
}
private void HandlePlayerSettingsSurfaceCommand(EveSurfaceCommandRequest request)
{
2026-06-21 09:37:54 +01:00
if (!AetheriaRuntimeMainMenuSurfaceCommands.TryRead(request, out var command))
{
2026-06-21 09:37:54 +01:00
Debug.LogWarning("Unknown player-settings command.");
return;
}
2026-06-21 09:37:54 +01:00
switch (command.Kind)
{
2026-06-21 09:37:54 +01:00
case AetheriaRuntimeMainMenuCommandKind.BackToSettings:
ShowSettings();
return;
case AetheriaRuntimeMainMenuCommandKind.PlayerSettingsCommand:
2026-06-21 14:23:32 +01:00
SendKnownAetheriaEveCommand(request, "player-settings");
2026-06-21 09:37:54 +01:00
ShowPlayerSettingsSurface();
return;
default:
Debug.LogWarning($"Unhandled player-settings command kind: {command.Kind}");
return;
}
}
private void HandleVerseSettingsSurfaceCommand(EveSurfaceCommandRequest request)
{
2026-06-21 09:37:54 +01:00
if (!AetheriaRuntimeMainMenuSurfaceCommands.TryRead(request, out var command))
{
2026-06-21 09:37:54 +01:00
Debug.LogWarning("Unknown verse-settings command.");
return;
}
2026-06-21 09:37:54 +01:00
switch (command.Kind)
{
2026-06-21 09:37:54 +01:00
case AetheriaRuntimeMainMenuCommandKind.BackToSettings:
ShowSettings();
return;
case AetheriaRuntimeMainMenuCommandKind.ClientTargetCommand:
2026-06-21 14:23:32 +01:00
RequestClientTargetCommand(request);
ShowVerseSettingsSurface();
2026-06-21 09:37:54 +01:00
return;
case AetheriaRuntimeMainMenuCommandKind.VerseHostCommand:
2026-06-21 14:23:32 +01:00
SendKnownAetheriaEveCommand(request, "Verse-host");
ShowVerseSettingsSurface();
2026-06-21 09:37:54 +01:00
return;
default:
Debug.LogWarning($"Unhandled verse-settings command kind: {command.Kind}");
return;
}
}
private void HideMenuSurface()
{
if (_menuSurfaceDocument == null)
return;
2026-06-21 09:37:54 +01:00
AetheriaEveUnitySurfaceHost.Hide(_menuSurfaceDocument);
}
2026-06-25 18:09:33 +01:00
public void SetRuntimeInputScreenShell(Func<bool> canOpenRuntimeInputScreen, Action openRuntimeInputScreen)
{
_canOpenRuntimeInputScreen = canOpenRuntimeInputScreen;
_openRuntimeInputScreen = openRuntimeInputScreen;
}
private bool CanOpenRuntimeInputScreen()
{
return InGame &&
2026-06-25 18:09:33 +01:00
_canOpenRuntimeInputScreen?.Invoke() == true;
}
private bool TryOpenRuntimeInputScreen()
{
if (!CanOpenRuntimeInputScreen())
return false;
HideMenuSurface();
gameObject.SetActive(false);
2026-06-25 18:09:33 +01:00
_openRuntimeInputScreen?.Invoke();
return true;
}
2026-06-21 14:23:32 +01:00
private static void RequestClientTargetCommand(EveSurfaceCommandRequest request)
{
try
{
2026-06-21 09:37:54 +01:00
if (!AetheriaRuntimeClientTargetSurfaceCommands.TryRequest(
2026-06-25 18:09:33 +01:00
AetheriaState.At(AetheriaUnityRuntimePaths.GameDataDirectory).ClientTarget,
2026-06-21 09:37:54 +01:00
request,
out _))
{
2026-06-21 14:23:32 +01:00
return;
}
}
catch (Exception ex)
{
Debug.LogError($"Failed to update typed Aetheria client target: {ex}");
}
}
private void SendKnownAetheriaEveCommand(
2026-06-21 09:37:54 +01:00
EveSurfaceCommandRequest request,
string label)
{
2026-06-21 09:37:54 +01:00
if (request == null)
2026-06-21 14:23:32 +01:00
return;
2026-06-21 09:37:54 +01:00
var stateBoot = CurrentStateBoot();
if (!CanSendLocalEveCommand(stateBoot, label))
2026-06-21 14:23:32 +01:00
return;
2026-06-21 09:37:54 +01:00
try
{
2026-06-25 18:09:33 +01:00
var submitted = ResolveClient(stateBoot)
.Ui.SurfaceCommandAsync(request, "unity-main-menu")
.GetAwaiter()
.GetResult();
2026-06-25 18:09:33 +01:00
Debug.Log($"Submitted Aetheria {label} Eve operation: {submitted.OperationId}");
}
2026-06-21 09:37:54 +01:00
catch (Exception ex)
{
Debug.LogError($"Failed to send Aetheria {label} Eve command: {ex}");
}
}
2026-06-21 09:37:54 +01:00
private static bool CanSendLocalEveCommand(
AetheriaRuntimeStateBootReport stateBoot,
string label)
{
2026-06-21 09:37:54 +01:00
if (!stateBoot.SupportsLocalStateFileRead || !stateBoot.StateFileExists)
{
Debug.LogWarning(
$"Cannot send {label} command because the active target is not a readable local Verse state file.");
2026-06-21 09:37:54 +01:00
return false;
}
2026-06-21 09:37:54 +01:00
return true;
}
2026-06-25 18:09:33 +01:00
private AetheriaClient ResolveClient(AetheriaRuntimeStateBootReport stateBoot)
{
var statePath = stateBoot.StateFilePath;
if (!string.Equals(_clientStatePath, statePath, StringComparison.Ordinal))
{
_clientStatePath = statePath;
ClearClientCaches();
}
return AetheriaUnityRuntimeClientProvider.ResolveClient(stateBoot, "unity-main-menu");
}
private void ClearClientCaches()
{
_sectorMap?.Dispose();
_playerSettings?.Dispose();
_verseHostSettings?.Dispose();
_sectorMap = null;
_playerSettings = null;
_verseHostSettings = null;
}
private void OnDestroy()
{
ClearClientCaches();
if (_menuSurfaceDocument != null)
{
Destroy(_menuSurfaceDocument.gameObject);
_menuSurfaceDocument = null;
}
}
}