Move main menu surface access to CultMesh
This commit is contained in:
parent
764f7c25c9
commit
eb0760c81f
5 changed files with 265 additions and 150 deletions
|
|
@ -3,16 +3,12 @@ using System.Collections.Generic;
|
|||
using GameCult.Aetheria.EveRuntime;
|
||||
using GameCult.Aetheria.State.Verse;
|
||||
using GameCult.Eve.Surface;
|
||||
using GameCult.Mesh;
|
||||
using TMPro;
|
||||
using UniRx;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
using UnityEngine.UI;
|
||||
using UnityEngine.UIElements;
|
||||
using UiButton = UnityEngine.UIElements.Button;
|
||||
using TextCoreFontAsset = UnityEngine.TextCore.Text.FontAsset;
|
||||
using GlyphRenderMode = UnityEngine.TextCore.LowLevel.GlyphRenderMode;
|
||||
|
||||
public class MainMenu : MonoBehaviour
|
||||
{
|
||||
|
|
@ -35,8 +31,6 @@ public class MainMenu : MonoBehaviour
|
|||
private bool _fading;
|
||||
private float _fadeLerp;
|
||||
private readonly List<MenuHoverButton> _hoverButtons = new List<MenuHoverButton>();
|
||||
private static TextCoreFontAsset _titleFont;
|
||||
private static TextCoreFontAsset _labelFont;
|
||||
private readonly AetheriaEveUnitySurfaceChrome _menuSurfaceChrome = new AetheriaEveUnitySurfaceChrome
|
||||
{
|
||||
UseShell = false,
|
||||
|
|
@ -63,19 +57,8 @@ public class MainMenu : MonoBehaviour
|
|||
|
||||
private void ShowMain(bool animateFromRight = true)
|
||||
{
|
||||
var stateBoot = CurrentStateBoot();
|
||||
var sectorMap = ResolveSectorMap(stateBoot);
|
||||
var verseHost = ResolveVerseHostSettings(stateBoot);
|
||||
var playerSettings = ResolvePlayerSettings(stateBoot);
|
||||
RenderMenuSurface(
|
||||
AetheriaRuntimeMainMenuSurfaceBuilder.BuildRoot(
|
||||
stateBoot,
|
||||
sectorMap,
|
||||
verseHost,
|
||||
playerSettings,
|
||||
CanOpenRuntimeInputScreen(),
|
||||
InGame,
|
||||
DateTime.UtcNow.ToString("O")),
|
||||
ResolveMainMenuSurface(AetheriaRuntimeMainMenuCommands.RootSurfaceId),
|
||||
HandleMainSurfaceCommand,
|
||||
animateFromRight);
|
||||
}
|
||||
|
|
@ -177,44 +160,6 @@ public class MainMenu : MonoBehaviour
|
|||
}
|
||||
}
|
||||
|
||||
private AetheriaRuntimePlayerSettingsDocument ResolvePlayerSettings(AetheriaRuntimeStateBootReport stateBoot)
|
||||
{
|
||||
if (!stateBoot.SupportsLocalStateFileRead || !stateBoot.StateFileExists)
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
return AetheriaUnityRuntimeClientProvider
|
||||
.RuntimeState(stateBoot, "unity-main-menu")
|
||||
.PlayerSettings
|
||||
.Latest();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogError($"Failed to bind typed Aetheria player settings for the main menu: {ex}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private AetheriaRuntimeVerseHostSettingsDocument ResolveVerseHostSettings(AetheriaRuntimeStateBootReport stateBoot)
|
||||
{
|
||||
if (!stateBoot.SupportsLocalStateFileRead || !stateBoot.StateFileExists)
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
return AetheriaUnityRuntimeClientProvider
|
||||
.RuntimeState(stateBoot, "unity-main-menu")
|
||||
.VerseHostSettings
|
||||
.Latest();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogError($"Failed to bind typed Aetheria Verse host settings for the main menu: {ex}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsMissingDaemonFrame(KeyNotFoundException ex)
|
||||
{
|
||||
return ex?.Message?.Contains("daemon:aetheria.frame.latest.v1", StringComparison.Ordinal) == true;
|
||||
|
|
@ -231,21 +176,15 @@ public class MainMenu : MonoBehaviour
|
|||
private void ShowSettings(bool animateFromRight = true)
|
||||
{
|
||||
RenderMenuSurface(
|
||||
AetheriaRuntimeMainMenuSurfaceBuilder.BuildSettings(DateTime.UtcNow.ToString("O")),
|
||||
ResolveMainMenuSurface(AetheriaRuntimeMainMenuCommands.SettingsSurfaceId),
|
||||
HandleSettingsSurfaceCommand,
|
||||
animateFromRight);
|
||||
}
|
||||
|
||||
private void ShowInputSettings(bool animateFromRight = true)
|
||||
{
|
||||
var stateBoot = CurrentStateBoot();
|
||||
RenderMenuSurface(
|
||||
AetheriaRuntimeMainMenuSurfaceBuilder.BuildInputSettings(
|
||||
stateBoot,
|
||||
ResolvePlayerSettings(stateBoot),
|
||||
CanOpenRuntimeInputScreen(),
|
||||
InGame,
|
||||
DateTime.UtcNow.ToString("O")),
|
||||
ResolveMainMenuSurface(AetheriaRuntimeMainMenuCommands.InputSettingsSurfaceId),
|
||||
HandleInputSettingsSurfaceCommand,
|
||||
animateFromRight);
|
||||
}
|
||||
|
|
@ -253,26 +192,28 @@ public class MainMenu : MonoBehaviour
|
|||
private void ShowPlayerSettingsSurface(bool animateFromRight = true)
|
||||
{
|
||||
RenderMenuSurface(
|
||||
AetheriaRuntimeMainMenuSurfaceBuilder.BuildPlayerSettingsShell(
|
||||
ResolvePlayerSettings(CurrentStateBoot()),
|
||||
DateTime.UtcNow.ToString("O")),
|
||||
ResolveMainMenuSurface(AetheriaRuntimeMainMenuCommands.PlayerSettingsShellSurfaceId),
|
||||
HandlePlayerSettingsSurfaceCommand,
|
||||
animateFromRight);
|
||||
}
|
||||
|
||||
private void ShowVerseSettingsSurface(bool animateFromRight = true)
|
||||
{
|
||||
var stateBoot = CurrentStateBoot();
|
||||
RenderMenuSurface(
|
||||
AetheriaRuntimeMainMenuSurfaceBuilder.BuildVerseSettingsShell(
|
||||
AetheriaRuntimeClientTargetSurfaceBuilder.Build(
|
||||
stateBoot,
|
||||
ResolveVerseHostSettings(stateBoot),
|
||||
DateTime.UtcNow.ToString("O"))),
|
||||
ResolveMainMenuSurface(AetheriaRuntimeMainMenuCommands.VerseSettingsShellSurfaceId),
|
||||
HandleVerseSettingsSurfaceCommand,
|
||||
animateFromRight);
|
||||
}
|
||||
|
||||
private AetheriaRuntimeSurfaceDocument ResolveMainMenuSurface(string surfaceId)
|
||||
{
|
||||
var stateBoot = CurrentStateBoot();
|
||||
return AetheriaUnityRuntimeClientProvider
|
||||
.RuntimeState(stateBoot, "unity-main-menu")
|
||||
.MainMenuSurface(surfaceId, CanOpenRuntimeInputScreen(), InGame)
|
||||
.Latest();
|
||||
}
|
||||
|
||||
private void RenderMenuSurface(
|
||||
AetheriaRuntimeSurfaceDocument document,
|
||||
Action<EveSurfaceCommandRequest> commandHandler,
|
||||
|
|
@ -375,7 +316,6 @@ public class MainMenu : MonoBehaviour
|
|||
return;
|
||||
|
||||
StyleMenuTree(root, rootMainMenu);
|
||||
ApplyDefaultTextFont(root);
|
||||
|
||||
var surface = rootMainMenu
|
||||
? FindElement(root, $"{AetheriaRuntimeMainMenuCommands.RootSurfaceId}.root")
|
||||
|
|
@ -391,7 +331,6 @@ public class MainMenu : MonoBehaviour
|
|||
var title = FindElement(root, $"{AetheriaRuntimeMainMenuCommands.RootSurfaceId}.title") as Label;
|
||||
if (title != null)
|
||||
{
|
||||
title.style.unityFontDefinition = new StyleFontDefinition(TitleFont);
|
||||
title.style.fontSize = 86f;
|
||||
title.style.color = new Color(0.82f, 0.95f, 1f, 0.96f);
|
||||
title.style.unityFontStyleAndWeight = FontStyle.Normal;
|
||||
|
|
@ -404,7 +343,6 @@ public class MainMenu : MonoBehaviour
|
|||
var subtitle = FindElement(root, $"{AetheriaRuntimeMainMenuCommands.RootSurfaceId}.subtitle") as Label;
|
||||
if (subtitle != null)
|
||||
{
|
||||
subtitle.style.unityFontDefinition = new StyleFontDefinition(TitleFont);
|
||||
subtitle.style.fontSize = 39f;
|
||||
subtitle.style.color = new Color(0.82f, 0.95f, 1f, 0.92f);
|
||||
subtitle.style.unityFontStyleAndWeight = FontStyle.Normal;
|
||||
|
|
@ -417,12 +355,6 @@ public class MainMenu : MonoBehaviour
|
|||
RegisterHoverButtons(root);
|
||||
}
|
||||
|
||||
private static void ApplyDefaultTextFont(VisualElement root)
|
||||
{
|
||||
root.Query<TextElement>().ForEach(element =>
|
||||
element.style.unityFontDefinition = new StyleFontDefinition(LabelFont));
|
||||
}
|
||||
|
||||
private void StyleMenuTree(VisualElement element, bool rootMainMenu)
|
||||
{
|
||||
if (element == null)
|
||||
|
|
@ -508,7 +440,6 @@ public class MainMenu : MonoBehaviour
|
|||
private static void StyleMenuLabel(Label label, bool rootMainMenu)
|
||||
{
|
||||
label.style.color = new Color(0.86f, 0.98f, 1f, 0.94f);
|
||||
label.style.unityFontDefinition = new StyleFontDefinition(LabelFont);
|
||||
label.style.unityFontStyleAndWeight = FontStyle.Normal;
|
||||
label.style.unityTextAlign = TextAnchor.MiddleLeft;
|
||||
label.style.whiteSpace = WhiteSpace.Normal;
|
||||
|
|
@ -556,14 +487,12 @@ public class MainMenu : MonoBehaviour
|
|||
field.style.marginTop = 0f;
|
||||
field.style.marginBottom = rootMainMenu ? 4f : 8f;
|
||||
field.style.color = new Color(0.86f, 0.98f, 1f, 0.96f);
|
||||
field.style.unityFontDefinition = new StyleFontDefinition(LabelFont);
|
||||
field.style.fontSize = rootMainMenu ? 18f : 15f;
|
||||
field.pickingMode = PickingMode.Position;
|
||||
|
||||
field.labelElement.style.minWidth = rootMainMenu ? 100f : 190f;
|
||||
field.labelElement.style.width = rootMainMenu ? 100f : 190f;
|
||||
field.labelElement.style.color = new Color(0.78f, 0.92f, 0.98f, 0.84f);
|
||||
field.labelElement.style.unityFontDefinition = new StyleFontDefinition(LabelFont);
|
||||
}
|
||||
|
||||
private static void StyleMenuButton(UiButton button, bool rootMainMenu)
|
||||
|
|
@ -574,7 +503,6 @@ public class MainMenu : MonoBehaviour
|
|||
button.style.borderTopWidth = 0f;
|
||||
button.style.borderBottomWidth = 0f;
|
||||
button.style.color = new Color(0.86f, 0.98f, 1f, 0.96f);
|
||||
button.style.unityFontDefinition = new StyleFontDefinition(LabelFont);
|
||||
button.style.fontSize = rootMainMenu ? 24f : 16f;
|
||||
button.style.unityFontStyleAndWeight = FontStyle.Normal;
|
||||
button.style.unityTextAlign = TextAnchor.MiddleLeft;
|
||||
|
|
@ -862,60 +790,6 @@ public class MainMenu : MonoBehaviour
|
|||
}
|
||||
}
|
||||
|
||||
private static TextCoreFontAsset TitleFont =>
|
||||
_titleFont ??= ResolveFontAsset(
|
||||
"Assets/Fonts/Ubuntu/Montserrat-Thin SDF.asset",
|
||||
"Montserrat Thin",
|
||||
"Montserrat");
|
||||
|
||||
private static TextCoreFontAsset LabelFont =>
|
||||
_labelFont ??= ResolveFontAsset(
|
||||
"Assets/Fonts/Ubuntu/Ubuntu-R SDF.asset",
|
||||
"Ubuntu",
|
||||
"Ubuntu Regular");
|
||||
|
||||
private static TextCoreFontAsset ResolveFontAsset(string assetPath, params string[] osFontNames)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
foreach (var asset in UnityEditor.AssetDatabase.LoadAllAssetsAtPath(assetPath))
|
||||
{
|
||||
if (asset is TextCoreFontAsset editorFontAsset)
|
||||
return editorFontAsset;
|
||||
}
|
||||
#endif
|
||||
|
||||
foreach (var osFontName in osFontNames)
|
||||
{
|
||||
try
|
||||
{
|
||||
var fontAsset = TextCoreFontAsset.CreateFontAsset(
|
||||
osFontName,
|
||||
"Regular",
|
||||
90,
|
||||
9,
|
||||
GlyphRenderMode.SDFAA);
|
||||
if (fontAsset != null)
|
||||
return fontAsset;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Try the next configured face name before falling back.
|
||||
}
|
||||
}
|
||||
|
||||
Debug.LogWarning($"Failed to resolve UI font asset '{assetPath}'. Falling back to Arial.");
|
||||
var fallback = TextCoreFontAsset.CreateFontAsset(
|
||||
"Arial",
|
||||
"Regular",
|
||||
90,
|
||||
9,
|
||||
GlyphRenderMode.SDFAA);
|
||||
if (fallback != null)
|
||||
return fallback;
|
||||
|
||||
throw new InvalidOperationException($"Failed to create fallback UI font asset for '{assetPath}'.");
|
||||
}
|
||||
|
||||
private sealed class MenuHoverButton
|
||||
{
|
||||
public MenuHoverButton(UiButton button)
|
||||
|
|
|
|||
|
|
@ -6,7 +6,9 @@ using GameCult.Eve.Surface;
|
|||
using GameCult.Eve.UnityUIToolkit;
|
||||
using GameCult.Mesh;
|
||||
using UnityEngine;
|
||||
using UnityEngine.TextCore.LowLevel;
|
||||
using UnityEngine.UIElements;
|
||||
using TextCoreFontAsset = UnityEngine.TextCore.Text.FontAsset;
|
||||
|
||||
namespace GameCult.Aetheria.EveRuntime
|
||||
{
|
||||
|
|
@ -42,6 +44,7 @@ namespace GameCult.Aetheria.EveRuntime
|
|||
public static class AetheriaEveUnitySurfaceHost
|
||||
{
|
||||
private static PanelSettings _runtimePanelSettings;
|
||||
private static readonly Dictionary<string, TextCoreFontAsset> FontCache = new Dictionary<string, TextCoreFontAsset>(StringComparer.Ordinal);
|
||||
|
||||
public static UIDocument Render(
|
||||
Transform owner,
|
||||
|
|
@ -73,15 +76,20 @@ namespace GameCult.Aetheria.EveRuntime
|
|||
: null);
|
||||
surface = AetheriaRuntimeSurfaceDocuments.ResolveStateRefs(surface, effectiveStateRefResolver);
|
||||
var lowerer = new EveUiToolkitSurfaceLowerer(new EveUiToolkitSurfaceOptions(embeddedDocumentResolver));
|
||||
VisualElement lowered;
|
||||
if (chrome.UseShell)
|
||||
{
|
||||
var shell = CreateShell(chrome);
|
||||
root.Add(shell);
|
||||
shell.Add(lowerer.Lower(surface, commandHandler));
|
||||
lowered = lowerer.Lower(surface, commandHandler);
|
||||
ApplyTheme(lowered, surface);
|
||||
shell.Add(lowered);
|
||||
}
|
||||
else
|
||||
{
|
||||
root.Add(lowerer.Lower(surface, commandHandler));
|
||||
lowered = lowerer.Lower(surface, commandHandler);
|
||||
ApplyTheme(lowered, surface);
|
||||
root.Add(lowered);
|
||||
}
|
||||
|
||||
Debug.Log(
|
||||
|
|
@ -230,6 +238,75 @@ namespace GameCult.Aetheria.EveRuntime
|
|||
return shell;
|
||||
}
|
||||
|
||||
private static void ApplyTheme(VisualElement root, EveSurfaceDocument surface)
|
||||
{
|
||||
if (root == null || surface?.Surface?.Styles == null)
|
||||
return;
|
||||
|
||||
var tokens = surface.Surface.Styles
|
||||
.Where(token => !string.IsNullOrWhiteSpace(token?.Name))
|
||||
.GroupBy(token => token.Name, StringComparer.Ordinal)
|
||||
.ToDictionary(group => group.Key, group => group.Last().Value ?? "", StringComparer.Ordinal);
|
||||
var bodyFont = ResolveFont(tokens, "body");
|
||||
var titleFont = ResolveFont(tokens, "title") ?? bodyFont;
|
||||
|
||||
if (bodyFont != null)
|
||||
{
|
||||
root.Query<TextElement>().ForEach(element =>
|
||||
element.style.unityFontDefinition = new StyleFontDefinition(bodyFont));
|
||||
}
|
||||
|
||||
if (titleFont != null)
|
||||
{
|
||||
root.Query<TextElement>(className: "eve-kind-text-title").ForEach(element =>
|
||||
element.style.unityFontDefinition = new StyleFontDefinition(titleFont));
|
||||
}
|
||||
}
|
||||
|
||||
private static TextCoreFontAsset ResolveFont(IReadOnlyDictionary<string, string> tokens, string role)
|
||||
{
|
||||
if (tokens == null || string.IsNullOrWhiteSpace(role))
|
||||
return null;
|
||||
|
||||
if (!tokens.TryGetValue($"font.{role}.family", out var family) || string.IsNullOrWhiteSpace(family))
|
||||
return null;
|
||||
|
||||
tokens.TryGetValue($"font.{role}.style", out var style);
|
||||
style = string.IsNullOrWhiteSpace(style) ? "Regular" : style;
|
||||
var key = $"{family}\n{style}";
|
||||
if (FontCache.TryGetValue(key, out var cached))
|
||||
return cached;
|
||||
|
||||
var font = TryCreateFontAsset(family, style)
|
||||
?? TryCreateFontAsset(family, "Regular")
|
||||
?? TryCreateFontAsset($"{family} {style}", "Regular");
|
||||
if (font == null)
|
||||
{
|
||||
Debug.LogWarning($"Aetheria Eve UI could not resolve runtime font '{family}' style '{style}'.");
|
||||
return null;
|
||||
}
|
||||
|
||||
FontCache[key] = font;
|
||||
return font;
|
||||
}
|
||||
|
||||
private static TextCoreFontAsset TryCreateFontAsset(string family, string style)
|
||||
{
|
||||
try
|
||||
{
|
||||
return TextCoreFontAsset.CreateFontAsset(
|
||||
family,
|
||||
string.IsNullOrWhiteSpace(style) ? "Regular" : style,
|
||||
90,
|
||||
9,
|
||||
GlyphRenderMode.SDFAA);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool ContainsStateRefs(EveSurfaceDocument surface)
|
||||
{
|
||||
return surface?.Surface?.Root != null && ContainsStateRefs(surface.Surface.Root);
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ namespace GameCult.Aetheria.State.Verse
|
|||
private readonly Func<int, CultMeshDocumentHandle<AetheriaRuntimeSurfaceDocument>> _zoneDetailsSurface;
|
||||
private readonly Func<AetheriaRuntimeInventoryPanelSurfaceRequest, CultMeshDocumentHandle<AetheriaRuntimeSurfaceDocument>> _inventoryPanelSurface;
|
||||
private readonly Func<AetheriaRuntimeInventoryDropdownSurfaceRequest, CultMeshDocumentHandle<AetheriaRuntimeSurfaceDocument>> _inventoryDropdownSurface;
|
||||
private readonly Func<string, bool, bool, CultMeshDocumentHandle<AetheriaRuntimeSurfaceDocument>> _mainMenuSurface;
|
||||
private readonly Func<int, CultMeshDocumentHandle<AetheriaRuntimeSelectedObjectDocument>> _selectedObject;
|
||||
private readonly Func<int, CultMeshDocumentHandle<AetheriaRuntimeInventoryDocument>> _inventory;
|
||||
private readonly Func<string, CultMeshDocumentHandle<AetheriaRuntimeStarbridgePlayerSeatDocument>> _starbridgePlayerSeat;
|
||||
|
|
@ -63,6 +64,7 @@ namespace GameCult.Aetheria.State.Verse
|
|||
Func<int, CultMeshDocumentHandle<AetheriaRuntimeSurfaceDocument>> zoneDetailsSurface,
|
||||
Func<AetheriaRuntimeInventoryPanelSurfaceRequest, CultMeshDocumentHandle<AetheriaRuntimeSurfaceDocument>> inventoryPanelSurface,
|
||||
Func<AetheriaRuntimeInventoryDropdownSurfaceRequest, CultMeshDocumentHandle<AetheriaRuntimeSurfaceDocument>> inventoryDropdownSurface,
|
||||
Func<string, bool, bool, CultMeshDocumentHandle<AetheriaRuntimeSurfaceDocument>> mainMenuSurface,
|
||||
Func<int, CultMeshDocumentHandle<AetheriaRuntimeSelectedObjectDocument>> selectedObject,
|
||||
Func<int, CultMeshDocumentHandle<AetheriaRuntimeInventoryDocument>> inventory,
|
||||
CultMeshDocumentHandle<AetheriaRuntimeStarbridgeScenarioDocument> starbridgeScenario,
|
||||
|
|
@ -99,6 +101,7 @@ namespace GameCult.Aetheria.State.Verse
|
|||
_zoneDetailsSurface = zoneDetailsSurface ?? throw new ArgumentNullException(nameof(zoneDetailsSurface));
|
||||
_inventoryPanelSurface = inventoryPanelSurface ?? throw new ArgumentNullException(nameof(inventoryPanelSurface));
|
||||
_inventoryDropdownSurface = inventoryDropdownSurface ?? throw new ArgumentNullException(nameof(inventoryDropdownSurface));
|
||||
_mainMenuSurface = mainMenuSurface ?? throw new ArgumentNullException(nameof(mainMenuSurface));
|
||||
_selectedObject = selectedObject ?? throw new ArgumentNullException(nameof(selectedObject));
|
||||
_inventory = inventory ?? throw new ArgumentNullException(nameof(inventory));
|
||||
StarbridgeScenario = starbridgeScenario ?? throw new ArgumentNullException(nameof(starbridgeScenario));
|
||||
|
|
@ -255,6 +258,17 @@ namespace GameCult.Aetheria.State.Verse
|
|||
return _inventoryDropdownSurface(request ?? new AetheriaRuntimeInventoryDropdownSurfaceRequest());
|
||||
}
|
||||
|
||||
public CultMeshDocumentHandle<AetheriaRuntimeSurfaceDocument> MainMenuSurface(
|
||||
string surfaceId,
|
||||
bool canOpenRuntimeInputScreen,
|
||||
bool inGame)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(surfaceId))
|
||||
throw new ArgumentException("Main menu surface id must be non-empty.", nameof(surfaceId));
|
||||
|
||||
return _mainMenuSurface(surfaceId, canOpenRuntimeInputScreen, inGame);
|
||||
}
|
||||
|
||||
public CultMeshDocumentHandle<AetheriaRuntimeSelectedObjectDocument> SelectedObject(int zoneIndex)
|
||||
{
|
||||
if (zoneIndex < 0)
|
||||
|
|
|
|||
|
|
@ -27,6 +27,9 @@ namespace GameCult.Aetheria.State.Verse
|
|||
|
||||
public static class AetheriaRuntimeMainMenuSurfaceBuilder
|
||||
{
|
||||
public const string ProviderId = "aetheria";
|
||||
public const string ProviderKind = "game.menu";
|
||||
|
||||
public static AetheriaRuntimeSurfaceDocument BuildRoot(
|
||||
AetheriaRuntimeStateBootReport stateBoot,
|
||||
AetheriaRuntimePlayerSettingsDocument playerSettings,
|
||||
|
|
@ -327,18 +330,34 @@ namespace GameCult.Aetheria.State.Verse
|
|||
params AetheriaRuntimeSurfaceComponent[] children)
|
||||
{
|
||||
return new AetheriaRuntimeSurfaceDocument(
|
||||
providerId: "aetheria",
|
||||
providerKind: "game.menu",
|
||||
providerId: ProviderId,
|
||||
providerKind: ProviderKind,
|
||||
title: title,
|
||||
version: version,
|
||||
updatedAtUtc: updatedAtUtc,
|
||||
surface: new AetheriaRuntimeSurfaceTree(
|
||||
surfaceId,
|
||||
Node($"{surfaceId}.root", "surface", Array.Empty<(string Key, string Value)>(), children),
|
||||
Array.Empty<AetheriaRuntimeSurfaceStyleToken>()),
|
||||
MainMenuStyleTokens()),
|
||||
commands: commands);
|
||||
}
|
||||
|
||||
private static IReadOnlyList<AetheriaRuntimeSurfaceStyleToken> MainMenuStyleTokens()
|
||||
{
|
||||
return new[]
|
||||
{
|
||||
new AetheriaRuntimeSurfaceStyleToken("font.title.family", "Montserrat"),
|
||||
new AetheriaRuntimeSurfaceStyleToken("font.title.style", "Thin"),
|
||||
new AetheriaRuntimeSurfaceStyleToken("font.title.weight", "100"),
|
||||
new AetheriaRuntimeSurfaceStyleToken("font.body.family", "Ubuntu"),
|
||||
new AetheriaRuntimeSurfaceStyleToken("font.body.style", "Regular"),
|
||||
new AetheriaRuntimeSurfaceStyleToken("font.body.weight", "400"),
|
||||
new AetheriaRuntimeSurfaceStyleToken(
|
||||
"font.web.google",
|
||||
"https://fonts.googleapis.com/css2?family=Montserrat:wght@100&family=Ubuntu:wght@400&display=swap")
|
||||
};
|
||||
}
|
||||
|
||||
private static AetheriaRuntimeSurfaceCommandTemplate Command(string command, string label)
|
||||
{
|
||||
return new AetheriaRuntimeSurfaceCommandTemplate(command, label, AetheriaRuntimeSurfaceCommandTemplate.CultMeshTransport);
|
||||
|
|
|
|||
|
|
@ -391,6 +391,11 @@ namespace GameCult.Aetheria.State.Verse
|
|||
() => Task.FromResult(BootstrapPlayerSettingsDocument()),
|
||||
AetheriaRuntimePlayerSettingsDocument.SchemaId,
|
||||
CatalogBootstrapSource("catalog:aetheria.player_settings"));
|
||||
var verseHostSettingsDocument = BootstrapCatalogDocument(
|
||||
"aetheria.settings.verse_host",
|
||||
() => Task.FromResult(BootstrapVerseHostSettingsDocument()),
|
||||
AetheriaRuntimeVerseHostSettingsDocument.SchemaId,
|
||||
CatalogBootstrapSource("catalog:aetheria.verse_host_settings"));
|
||||
var starbridgeScenarioDocument = Document<AetheriaRuntimeStarbridgeScenarioDocument>(
|
||||
AetheriaRuntimeVerseRecordKeys.StarbridgeScenarioLatest);
|
||||
var starbridgeSessionDocument = Document<AetheriaRuntimeStarbridgeSessionDocument>(
|
||||
|
|
@ -427,11 +432,7 @@ namespace GameCult.Aetheria.State.Verse
|
|||
catalogDocument,
|
||||
loadoutTemplatesDocument,
|
||||
playerSettingsDocument,
|
||||
BootstrapCatalogDocument(
|
||||
"aetheria.settings.verse_host",
|
||||
() => Task.FromResult(BootstrapVerseHostSettingsDocument()),
|
||||
AetheriaRuntimeVerseHostSettingsDocument.SchemaId,
|
||||
CatalogBootstrapSource("catalog:aetheria.verse_host_settings")),
|
||||
verseHostSettingsDocument,
|
||||
ManagedFrameDocument(
|
||||
"aetheria.current.zone",
|
||||
frame => Task.FromResult(AetheriaRuntimeRtsDocuments.CurrentZone(frame)),
|
||||
|
|
@ -501,6 +502,10 @@ namespace GameCult.Aetheria.State.Verse
|
|||
AetheriaRuntimeInventoryDropdownSurfaceBuilder.SurfaceId,
|
||||
CultMesh.ProjectionSource("catalog:aetheria.runtime"),
|
||||
CultMesh.ProjectionSource("loadout-templates:aetheria.runtime")),
|
||||
(surfaceId, canOpenRuntimeInputScreen, inGame) => ManagedMainMenuSurfaceDocument(
|
||||
surfaceId,
|
||||
canOpenRuntimeInputScreen,
|
||||
inGame),
|
||||
entityIndex => ManagedFrameDocument(
|
||||
IndexedDocumentId("aetheria.object.selected", entityIndex),
|
||||
frame => Task.FromResult(AetheriaRuntimeRtsDocuments.SelectedObject(frame, entityIndex)),
|
||||
|
|
@ -608,6 +613,39 @@ namespace GameCult.Aetheria.State.Verse
|
|||
routeHint: new CultMeshRouteHint(CultMeshLocalityKind.SharedMemory, "Aetheria managed catalog state"));
|
||||
}
|
||||
|
||||
CultMeshDocumentHandle<AetheriaRuntimeSurfaceDocument> ManagedMainMenuSurfaceDocument(
|
||||
string surfaceId,
|
||||
bool canOpenRuntimeInputScreen,
|
||||
bool inGame)
|
||||
{
|
||||
surfaceId = string.IsNullOrWhiteSpace(surfaceId)
|
||||
? AetheriaRuntimeMainMenuCommands.RootSurfaceId
|
||||
: surfaceId;
|
||||
var documentId = string.Join(
|
||||
".",
|
||||
"aetheria.main_menu.surface",
|
||||
AetheriaRuntimeVerseRecordKeys.StableToken(surfaceId),
|
||||
canOpenRuntimeInputScreen ? "input-open" : "input-closed",
|
||||
inGame ? "in-game" : "title");
|
||||
var descriptor = CultDocumentRegistry.Shared.GetRequired<AetheriaRuntimeSurfaceDocument>();
|
||||
var sources = new[]
|
||||
{
|
||||
CultMesh.ProjectionSource(documentId, descriptor.SchemaId, "managed Aetheria main menu surface"),
|
||||
CultMesh.ProjectionSource("catalog:aetheria.player_settings"),
|
||||
CultMesh.ProjectionSource("catalog:aetheria.verse_host_settings"),
|
||||
CultMesh.ProjectionSource(AetheriaRuntimeVerseRecordKeys.DaemonFrameLatest.ToString())
|
||||
};
|
||||
var verse = CultMesh.Verse("aetheria.local", RuntimeId);
|
||||
return CultMesh.Document(
|
||||
documentId,
|
||||
verse,
|
||||
_ => Task.FromResult(BuildMainMenuSurface(surfaceId, canOpenRuntimeInputScreen, inGame)),
|
||||
_ => frameChanges
|
||||
.Select(_ => BuildMainMenuSurface(surfaceId, canOpenRuntimeInputScreen, inGame)),
|
||||
sources: sources,
|
||||
routeHint: new CultMeshRouteHint(CultMeshLocalityKind.SharedMemory, "Aetheria managed main menu surface"));
|
||||
}
|
||||
|
||||
Task<AetheriaRuntimeStationRefitDocument> StationRefitAsync(
|
||||
AetheriaRuntimeDaemonFrameDocument frame)
|
||||
{
|
||||
|
|
@ -714,6 +752,99 @@ namespace GameCult.Aetheria.State.Verse
|
|||
DateTime.UtcNow.ToString("O", CultureInfo.InvariantCulture)));
|
||||
}
|
||||
|
||||
AetheriaRuntimeSurfaceDocument BuildMainMenuSurface(
|
||||
string surfaceId,
|
||||
bool canOpenRuntimeInputScreen,
|
||||
bool inGame)
|
||||
{
|
||||
var updatedAtUtc = DateTime.UtcNow.ToString("O", CultureInfo.InvariantCulture);
|
||||
var stateBoot = AetheriaRuntimeStateBoot.Inspect(
|
||||
new DirectoryInfo(Path.GetDirectoryName(StatePath) ?? "."),
|
||||
StatePath);
|
||||
|
||||
if (string.Equals(surfaceId, AetheriaRuntimeMainMenuCommands.RootSurfaceId, StringComparison.Ordinal))
|
||||
{
|
||||
return AetheriaRuntimeMainMenuSurfaceBuilder.BuildRoot(
|
||||
stateBoot,
|
||||
TryCurrentSectorMap(),
|
||||
TryCurrentVerseHostSettings(),
|
||||
TryCurrentPlayerSettings(),
|
||||
canOpenRuntimeInputScreen,
|
||||
inGame,
|
||||
updatedAtUtc);
|
||||
}
|
||||
|
||||
if (string.Equals(surfaceId, AetheriaRuntimeMainMenuCommands.SettingsSurfaceId, StringComparison.Ordinal))
|
||||
return AetheriaRuntimeMainMenuSurfaceBuilder.BuildSettings(updatedAtUtc);
|
||||
|
||||
if (string.Equals(surfaceId, AetheriaRuntimeMainMenuCommands.InputSettingsSurfaceId, StringComparison.Ordinal))
|
||||
{
|
||||
return AetheriaRuntimeMainMenuSurfaceBuilder.BuildInputSettings(
|
||||
stateBoot,
|
||||
TryCurrentPlayerSettings(),
|
||||
canOpenRuntimeInputScreen,
|
||||
inGame,
|
||||
updatedAtUtc);
|
||||
}
|
||||
|
||||
if (string.Equals(surfaceId, AetheriaRuntimeMainMenuCommands.PlayerSettingsShellSurfaceId, StringComparison.Ordinal))
|
||||
{
|
||||
return AetheriaRuntimeMainMenuSurfaceBuilder.BuildPlayerSettingsShell(
|
||||
TryCurrentPlayerSettings(),
|
||||
updatedAtUtc);
|
||||
}
|
||||
|
||||
if (string.Equals(surfaceId, AetheriaRuntimeMainMenuCommands.VerseSettingsShellSurfaceId, StringComparison.Ordinal))
|
||||
{
|
||||
return AetheriaRuntimeMainMenuSurfaceBuilder.BuildVerseSettingsShell(
|
||||
AetheriaRuntimeClientTargetSurfaceBuilder.Build(
|
||||
stateBoot,
|
||||
TryCurrentVerseHostSettings(),
|
||||
updatedAtUtc));
|
||||
}
|
||||
|
||||
throw new ArgumentException($"Unknown Aetheria main menu surface id '{surfaceId}'.", nameof(surfaceId));
|
||||
}
|
||||
|
||||
AetheriaRuntimePlayerSettingsDocument? TryCurrentPlayerSettings()
|
||||
{
|
||||
try
|
||||
{
|
||||
managedPlayerSettings ??= playerSettingsDocument.Reactive();
|
||||
_managedPlayerSettings = managedPlayerSettings;
|
||||
return managedPlayerSettings.Current;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
AetheriaRuntimeVerseHostSettingsDocument? TryCurrentVerseHostSettings()
|
||||
{
|
||||
try
|
||||
{
|
||||
return verseHostSettingsDocument.Latest();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
AetheriaRuntimeSectorMapDocument? TryCurrentSectorMap()
|
||||
{
|
||||
try
|
||||
{
|
||||
var frame = latestFrameDocument.Latest();
|
||||
return frame == null ? null : AetheriaRuntimeRtsDocuments.SectorMap(frame);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static string IndexedDocumentId(string prefix, int index)
|
||||
{
|
||||
return $"{prefix}.{index.ToString(CultureInfo.InvariantCulture)}";
|
||||
|
|
|
|||
Loading…
Reference in a new issue