Aetheria/Assets/Scripts/UI/MainMenu.cs

501 lines
17 KiB
C#

using System;
using System.Linq;
using GameCult.Aetheria.EveRuntime;
using GameCult.Aetheria.State.Verse;
using GameCult.Eve.Surface;
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;
private UIDocument _menuSurfaceDocument;
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();
}
private void ShowMain()
{
var stateBoot = CurrentStateBoot();
var frame = LatestDaemonFrame(stateBoot);
var verseHost = LatestVerseHostSettings(stateBoot);
RenderMenuSurface(
AetheriaRuntimeMainMenuSurfaceBuilder.BuildRoot(
ProjectMainMenuSurfaceState(
stateBoot,
frame,
verseHost,
CanOpenRuntimeInputScreen(),
InGame)),
HandleMainSurfaceCommand);
}
private void HandleMainSurfaceCommand(EveSurfaceCommandRequest request)
{
if (!AetheriaRuntimeMainMenuSurfaceCommands.TryRead(request, out var command))
{
Debug.LogWarning("Unknown main menu command.");
return;
}
switch (command.Kind)
{
case AetheriaRuntimeMainMenuCommandKind.ContinueRun:
if (LatestDaemonFrame(CurrentStateBoot()) == null)
{
Debug.LogWarning("Main-menu Continue requested without an authoritative daemon frame.");
ShowMain();
return;
}
ContinueGame();
return;
case AetheriaRuntimeMainMenuCommandKind.NewGame:
StartNewGame();
return;
case AetheriaRuntimeMainMenuCommandKind.ShowSettings:
ShowSettings();
return;
case AetheriaRuntimeMainMenuCommandKind.Quit:
Application.Quit();
return;
default:
Debug.LogWarning($"Unhandled main menu command kind: {command.Kind}");
return;
}
}
private void StartNewGame()
{
if (!TryStartDaemonObservedGame("Starting Daemon Run"))
{
ShowMain();
}
}
private bool TryStartDaemonObservedGame(string title)
{
var stateBoot = CurrentStateBoot();
if (!AetheriaRuntimeStateReader.TryReadDaemonFrame(stateBoot.StateFilePath, out var frame) ||
frame == null ||
!frame.IsAuthoritative ||
frame.Run == null ||
frame.Run.Zones == null ||
frame.Run.Zones.Count == 0)
{
Debug.LogWarning($"Cannot start Aetheria observer scene without an authoritative daemon frame at {AetheriaRuntimeDaemonFrameStore.GetFramePath(stateBoot.StateFilePath)}.");
return false;
}
var generatorState = "Loading runtime catalog";
Action<string> setState = s => generatorState = s;
HideMenuSurface();
Dialog.Clear();
Dialog.Title.text = title;
Dialog.AddProperty(() => generatorState);
Dialog.Show();
setState($"Observing daemon frame {frame.FrameId}");
ActionGameManager.IsTutorial = frame.Run.IsTutorial;
var backgroundSettings = frame.Run.IsTutorial ? Settings.TutorialBackgroundSettings : Settings.SectorBackgroundSettings;
backgroundSettings.NoisePosition = frame.Run.GenerationSeed == 0 ? 1 : frame.Run.GenerationSeed;
ActionGameManager.ObservedGalaxy = Galaxy.ProjectObservedDaemonRun(
frame.Run,
backgroundSettings,
ActionGameManager.RuntimeCatalog,
Debug.Log);
SceneManager.LoadScene("ARPG");
return true;
}
private static AetheriaRuntimeStateBootReport CurrentStateBoot()
{
return AetheriaRuntimeStateBoot.Inspect(ActionGameManager.GameDataDirectory);
}
private static AetheriaRuntimeDaemonFrameDocument LatestDaemonFrame(AetheriaRuntimeStateBootReport stateBoot)
{
if (!AetheriaRuntimeStateReader.TryReadDaemonFrame(stateBoot.StateFilePath, out var frame) ||
frame == null ||
!frame.IsAuthoritative ||
frame.Run == null ||
frame.Run.Zones == null ||
frame.Run.Zones.Count == 0)
{
return null;
}
return frame;
}
private static AetheriaRuntimeVerseHostSettingsSnapshot LatestVerseHostSettings(AetheriaRuntimeStateBootReport stateBoot)
{
if (!stateBoot.SupportsLocalStateFileRead || !stateBoot.StateFileExists)
return null;
try
{
return AetheriaRuntimeStateReader.ReadVerseHostSettings(stateBoot.StateFilePath);
}
catch (Exception ex)
{
Debug.LogError($"Failed to read typed Aetheria Verse host settings for the main menu: {ex}");
return null;
}
}
private void ContinueGame()
{
if (!TryStartDaemonObservedGame("Continuing Daemon Run"))
{
ShowMain();
}
}
private void ShowSettings()
{
RenderMenuSurface(
AetheriaRuntimeMainMenuSurfaceBuilder.BuildSettings(DateTime.UtcNow.ToString("O")),
HandleSettingsSurfaceCommand);
}
private void ShowInputSettings()
{
RenderMenuSurface(
AetheriaRuntimeMainMenuSurfaceBuilder.BuildInputSettings(
ProjectMainMenuSurfaceState(
CurrentStateBoot(),
null,
null,
CanOpenRuntimeInputScreen(),
InGame)),
HandleInputSettingsSurfaceCommand);
}
private void ShowPlayerSettingsSurface()
{
RenderMenuSurface(
AetheriaRuntimeMainMenuSurfaceBuilder.BuildPlayerSettingsShell(ProjectPlayerSettingsSurfaceState()),
HandlePlayerSettingsSurfaceCommand);
}
private void ShowVerseSettingsSurface()
{
RenderMenuSurface(
AetheriaRuntimeMainMenuSurfaceBuilder.BuildVerseSettingsShell(ProjectVerseSettingsSurfaceState()),
HandleVerseSettingsSurfaceCommand);
}
private void RenderMenuSurface(
AetheriaRuntimeSurfaceDocument document,
Action<EveSurfaceCommandRequest> commandHandler)
{
_menuSurfaceDocument = AetheriaEveUnitySurfaceHost.RenderRuntime(
transform,
_menuSurfaceDocument,
"Aetheria Menu Surface",
document,
commandHandler,
_menuSurfaceChrome);
}
private void HandleSettingsSurfaceCommand(EveSurfaceCommandRequest request)
{
if (!AetheriaRuntimeMainMenuSurfaceCommands.TryRead(request, out var command))
{
Debug.LogWarning("Unknown settings menu command.");
return;
}
switch (command.Kind)
{
case AetheriaRuntimeMainMenuCommandKind.ShowPlayerSettings:
ShowPlayerSettingsSurface();
return;
case AetheriaRuntimeMainMenuCommandKind.ShowVerseSettings:
ShowVerseSettingsSurface();
return;
case AetheriaRuntimeMainMenuCommandKind.ShowInputSettings:
ShowInputSettings();
return;
case AetheriaRuntimeMainMenuCommandKind.BackToMain:
ShowMain();
return;
default:
Debug.LogWarning($"Unhandled settings menu command kind: {command.Kind}");
return;
}
}
private void HandleInputSettingsSurfaceCommand(EveSurfaceCommandRequest request)
{
if (!AetheriaRuntimeMainMenuSurfaceCommands.TryRead(request, out var command))
{
Debug.LogWarning("Unknown input settings command.");
return;
}
switch (command.Kind)
{
case AetheriaRuntimeMainMenuCommandKind.BackToSettings:
ShowSettings();
return;
case AetheriaRuntimeMainMenuCommandKind.OpenRuntimeInputScreen:
if (!TryOpenRuntimeInputScreen())
{
ShowInputSettings();
}
return;
default:
Debug.LogWarning($"Unhandled input settings command kind: {command.Kind}");
return;
}
}
private void HandlePlayerSettingsSurfaceCommand(EveSurfaceCommandRequest request)
{
if (!AetheriaRuntimeMainMenuSurfaceCommands.TryRead(request, out var command))
{
Debug.LogWarning("Unknown player-settings command.");
return;
}
switch (command.Kind)
{
case AetheriaRuntimeMainMenuCommandKind.BackToSettings:
ShowSettings();
return;
case AetheriaRuntimeMainMenuCommandKind.PlayerSettingsCommand:
SendKnownAetheriaEveCommand(request, "player-settings");
ShowPlayerSettingsSurface();
return;
default:
Debug.LogWarning($"Unhandled player-settings command kind: {command.Kind}");
return;
}
}
private void HandleVerseSettingsSurfaceCommand(EveSurfaceCommandRequest request)
{
if (!AetheriaRuntimeMainMenuSurfaceCommands.TryRead(request, out var command))
{
Debug.LogWarning("Unknown verse-settings command.");
return;
}
switch (command.Kind)
{
case AetheriaRuntimeMainMenuCommandKind.BackToSettings:
ShowSettings();
return;
case AetheriaRuntimeMainMenuCommandKind.ClientTargetCommand:
RequestClientTargetCommand(request);
ShowVerseSettingsSurface();
return;
case AetheriaRuntimeMainMenuCommandKind.VerseHostCommand:
SendKnownAetheriaEveCommand(request, "Verse-host");
ShowVerseSettingsSurface();
return;
default:
Debug.LogWarning($"Unhandled verse-settings command kind: {command.Kind}");
return;
}
}
private void HideMenuSurface()
{
if (_menuSurfaceDocument == null)
return;
AetheriaEveUnitySurfaceHost.Hide(_menuSurfaceDocument);
}
private static AetheriaRuntimePlayerSettingsSurfaceState ProjectPlayerSettingsSurfaceState()
{
return new AetheriaRuntimePlayerSettingsSurfaceState(
ActionGameManager.RuntimePlayerSettings.Name,
ActionGameManager.RuntimePlayerSettings.TutorialPassed,
"",
ActionGameManager.RuntimePlayerSettings.GameplaySettings.TemperatureUnit.ToString(),
Math.Max(0, ActionGameManager.RuntimePlayerSettings.GameplaySettings.SignificantDigits),
ActionGameManager.RuntimePlayerSettings.GraphicsSettings.NebulaQuality.ToString(),
ActionGameManager.RuntimePlayerSettings.GraphicsSettings.ShowAsteroidsInMinimap,
DateTime.UtcNow.ToString("O"));
}
private static AetheriaRuntimeClientTargetSurfaceState ProjectVerseSettingsSurfaceState()
{
var stateBoot = CurrentStateBoot();
var verseHost = LatestVerseHostSettings(stateBoot);
return new AetheriaRuntimeClientTargetSurfaceState(
stateBoot.TargetKind,
stateBoot.Title,
stateBoot.VerseId,
stateBoot.CultMeshAddress,
stateBoot.StateFilePath,
stateBoot.ReplicaStateFilePath,
string.Join(", ", stateBoot.DiscoveryEndpoints ?? Array.Empty<string>()),
stateBoot.DiscoveredVerses ?? Array.Empty<AetheriaRuntimeDiscoveredVerse>(),
stateBoot.LastDiscoveryAtUtc,
stateBoot.LastDiscoveryError,
stateBoot.LastReplicaSyncAtUtc,
stateBoot.LastReplicaSyncError,
stateBoot.TargetSource,
stateBoot.SupportsLocalStateFileRead,
stateBoot.FailureMessage,
verseHost?.Title ?? stateBoot.Title,
verseHost?.VerseId ?? stateBoot.VerseId,
verseHost?.Visibility ?? "unknown",
verseHost?.CultMeshAddress ?? stateBoot.CultMeshAddress,
DateTime.UtcNow.ToString("O"));
}
private static AetheriaRuntimeMainMenuSurfaceState ProjectMainMenuSurfaceState(
AetheriaRuntimeStateBootReport stateBoot,
AetheriaRuntimeDaemonFrameDocument daemonFrame,
AetheriaRuntimeVerseHostSettingsSnapshot verseHost,
bool canOpenRuntimeInputScreen,
bool inGame)
{
return new AetheriaRuntimeMainMenuSurfaceState(
stateBoot.TargetLabel,
stateBoot.TargetKind,
stateBoot.TargetSource,
verseHost?.Title ?? stateBoot.Title,
verseHost?.VerseId ?? stateBoot.VerseId,
verseHost?.Visibility ?? "unknown",
verseHost?.CultMeshAddress ?? stateBoot.CultMeshAddress,
inGame,
daemonFrame != null,
daemonFrame?.Run?.RunId ?? "",
daemonFrame?.FrameId ?? -1,
ActionGameManager.RuntimePlayerSettings.InputSettings.InputActionMap.Count,
ActionGameManager.RuntimePlayerSettings.InputSettings.ActionBarInputs.Count,
canOpenRuntimeInputScreen,
DateTime.UtcNow.ToString("O"));
}
private bool CanOpenRuntimeInputScreen()
{
return InGame &&
ActionGameManager.Instance != null &&
ActionGameManager.Instance.CanShowInputScreenFromMenu() &&
ActionGameManager.Instance.InputDisplayLayout != null;
}
private bool TryOpenRuntimeInputScreen()
{
if (!CanOpenRuntimeInputScreen())
return false;
HideMenuSurface();
gameObject.SetActive(false);
ActionGameManager.Instance.ShowInputScreenFromMenu();
return true;
}
private static void RequestClientTargetCommand(EveSurfaceCommandRequest request)
{
try
{
if (!AetheriaRuntimeClientTargetSurfaceCommands.TryRequest(
AetheriaState.At(ActionGameManager.GameDataDirectory).ClientTarget,
request,
out _))
{
return;
}
}
catch (Exception ex)
{
Debug.LogError($"Failed to update typed Aetheria client target: {ex}");
}
}
private static void SendKnownAetheriaEveCommand(
EveSurfaceCommandRequest request,
string label)
{
if (request == null)
return;
var stateBoot = CurrentStateBoot();
if (!CanSendLocalEveCommand(stateBoot, label))
return;
try
{
if (!AetheriaRuntimeEveCommands.TrySendKnownSurfaceCommand(
stateBoot.StateFilePath,
request,
"unity-main-menu",
out var submitted,
out var error))
{
Debug.LogError($"Failed to submit Aetheria {label} Eve command: {error}");
return;
}
Debug.Log($"Submitted Aetheria {label} Eve command: {submitted!.CommandId}");
}
catch (Exception ex)
{
Debug.LogError($"Failed to send Aetheria {label} Eve command: {ex}");
}
}
private static bool CanSendLocalEveCommand(
AetheriaRuntimeStateBootReport stateBoot,
string label)
{
if (!stateBoot.SupportsLocalStateFileRead || !stateBoot.StateFileExists)
{
Debug.LogWarning(
$"Cannot send {label} command because the active target is not a readable local Verse state file.");
return false;
}
return true;
}
private void OnDestroy()
{
if (_menuSurfaceDocument != null)
{
Destroy(_menuSurfaceDocument.gameObject);
_menuSurfaceDocument = null;
}
}
}