Aetheria/Assets/Scripts/UI/InputScreen/InputDisplayLayout.cs

412 lines
12 KiB
C#
Raw Normal View History

using System;
using System.Collections.Generic;
using System.Linq;
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;
2026-06-17 08:41:06 +01:00
using GameCult.Eve.Surface;
using UnityEngine;
using UnityEngine.InputSystem;
2026-06-17 08:41:06 +01:00
using UnityEngine.InputSystem.Utilities;
using UnityEngine.UIElements;
public class InputDisplayLayout : MonoBehaviour
{
2026-06-17 08:41:06 +01:00
private UIDocument _surfaceDocument;
private AetheriaInput _ownedInput;
2026-06-25 18:09:33 +01:00
private string _clientStatePath = "";
private AetheriaRuntimePlayerSettingsDocument _playerSettings;
2026-06-17 08:41:06 +01:00
private InputAction _captureAction;
private InputActionAsset _input;
private string _captureActionName = "";
private int _captureBindingIndex = -1;
private string _captureBindingLabel = "";
2026-06-21 09:37:54 +01:00
private readonly AetheriaEveUnitySurfaceChrome _surfaceChrome = new AetheriaEveUnitySurfaceChrome
{
RootAlignItems = Align.Center,
RootJustifyContent = Justify.Center,
RootPaddingLeft = 24f,
RootPaddingRight = 24f,
RootPaddingTop = 24f,
RootPaddingBottom = 24f,
RootBackgroundColor = new Color(0f, 0f, 0f, 0.72f),
Width = 1080f,
MinWidth = 0f,
MaxWidth = 1080f,
MaxHeight = 900f,
FlexGrow = 1f,
PaddingLeft = 20f,
PaddingRight = 20f,
PaddingTop = 20f,
PaddingBottom = 20f,
BorderRadius = 0f,
BorderWidth = 0f,
BackgroundColor = new Color(0.08f, 0.1f, 0.14f, 0.96f)
};
2026-06-17 08:41:06 +01:00
public InputActionAsset Input
{
get => _input;
set
{
_input = value;
if (isActiveAndEnabled)
{
RenderSurface();
}
}
}
2026-06-17 08:41:06 +01:00
private void Start()
{
2026-06-17 08:41:06 +01:00
EnsureInputAsset();
EnsureCaptureAction();
HideLegacyChildren();
RenderSurface();
}
2026-06-13 04:56:50 +01:00
2026-06-17 08:41:06 +01:00
private void OnEnable()
{
2026-06-17 08:41:06 +01:00
EnsureInputAsset();
EnsureCaptureAction();
HideLegacyChildren();
RenderSurface();
}
2026-06-17 08:41:06 +01:00
private void OnDisable()
{
ClearCapture();
if (_captureAction != null)
{
2026-06-17 08:41:06 +01:00
_captureAction.Disable();
}
2026-06-13 04:56:50 +01:00
2026-06-17 08:41:06 +01:00
if (_surfaceDocument != null)
{
2026-06-21 09:37:54 +01:00
AetheriaEveUnitySurfaceHost.Hide(_surfaceDocument);
}
2026-06-17 08:41:06 +01:00
}
2026-06-13 04:56:50 +01:00
2026-06-17 08:41:06 +01:00
private void OnDestroy()
{
_captureAction?.Dispose();
_captureAction = null;
_ownedInput?.Dispose();
_ownedInput = null;
if (_surfaceDocument != null)
{
2026-06-21 09:37:54 +01:00
AetheriaEveUnitySurfaceHost.DestroyDocument(_surfaceDocument);
2026-06-17 08:41:06 +01:00
_surfaceDocument = null;
}
2026-06-17 08:41:06 +01:00
}
2026-06-17 08:41:06 +01:00
private void EnsureInputAsset()
{
if (Input != null)
{
2026-06-17 08:41:06 +01:00
return;
}
2026-06-17 08:41:06 +01:00
_ownedInput ??= new AetheriaInput();
Input = _ownedInput.asset;
}
2026-06-17 08:41:06 +01:00
private void HideLegacyChildren()
{
2026-06-17 08:41:06 +01:00
for (var index = 0; index < transform.childCount; index++)
{
2026-06-17 08:41:06 +01:00
var child = transform.GetChild(index);
if (_surfaceDocument != null && child.gameObject == _surfaceDocument.gameObject)
{
2026-06-17 08:41:06 +01:00
continue;
}
2026-06-17 08:41:06 +01:00
child.gameObject.SetActive(false);
}
}
2026-06-17 08:41:06 +01:00
private void EnsureCaptureAction()
{
2026-06-17 08:41:06 +01:00
if (_captureAction != null)
{
2026-06-17 08:41:06 +01:00
return;
}
2026-06-17 08:41:06 +01:00
_captureAction = new InputAction("Aetheria Input Capture");
_captureAction.AddBinding("<Keyboard>/anyKey");
_captureAction.AddBinding("<Mouse>/leftButton");
_captureAction.AddBinding("<Mouse>/rightButton");
_captureAction.AddBinding("<Mouse>/middleButton");
_captureAction.AddBinding("<Mouse>/forwardButton");
_captureAction.AddBinding("<Mouse>/backButton");
_captureAction.performed += OnCapturePerformed;
}
2026-06-17 08:41:06 +01:00
private void RenderSurface()
{
2026-06-17 08:41:06 +01:00
if (!isActiveAndEnabled)
{
return;
}
EnsureInputAsset();
EnsureCaptureAction();
2026-06-21 09:37:54 +01:00
var document = AetheriaRuntimeInputSettingsSurfaceBuilder.Build(ProjectSurfaceState());
_surfaceDocument = AetheriaEveUnitySurfaceHost.RenderRuntime(
transform,
_surfaceDocument,
"Aetheria Input Surface",
document,
HandleSurfaceCommand,
_surfaceChrome);
}
2026-06-17 08:41:06 +01:00
private AetheriaRuntimeInputSettingsSurfaceState ProjectSurfaceState()
{
2026-06-25 18:09:33 +01:00
var runtimeSettings = ResolvePlayerSettings();
return AetheriaRuntimeInputSettingsSurfaceBuilder.Project(
ProjectObservedBindings(),
2026-06-25 18:09:33 +01:00
runtimeSettings?.ActionBarInputs ?? Array.Empty<string>(),
2026-06-17 08:41:06 +01:00
capturePending: _captureBindingIndex >= 0 && !string.IsNullOrWhiteSpace(_captureActionName),
capturePrompt: BuildCapturePrompt(),
updatedAtUtc: DateTime.UtcNow.ToString("O"));
}
private IReadOnlyList<AetheriaRuntimeObservedInputBinding> ProjectObservedBindings()
{
2026-06-17 08:41:06 +01:00
if (Input == null)
{
return Array.Empty<AetheriaRuntimeObservedInputBinding>();
}
2026-06-17 08:41:06 +01:00
return Input
.Where(action => action.actionMap.name != "UI")
.SelectMany(action =>
action.bindings
.Select((binding, bindingIndex) => new { action, binding, bindingIndex }))
.Where(entry =>
!entry.binding.isComposite &&
!string.IsNullOrWhiteSpace(entry.binding.effectivePath) &&
2026-06-21 21:20:16 +01:00
AetheriaRuntimeInputSettingsSurfaceBuilder.IsSupportedCapturePath(entry.binding.effectivePath))
.Select(entry => new AetheriaRuntimeObservedInputBinding(
2026-06-17 08:41:06 +01:00
entry.action.name,
entry.bindingIndex,
DescribeBinding(entry.action.name, entry.binding),
entry.binding.effectivePath,
DescribeInputPath(entry.binding.effectivePath),
include: true))
2026-06-17 08:41:06 +01:00
.ToArray();
}
2026-06-17 08:41:06 +01:00
private static string DescribeBinding(string actionName, InputBinding binding)
{
2026-06-17 08:41:06 +01:00
if (binding.isPartOfComposite && !string.IsNullOrWhiteSpace(binding.name))
{
2026-06-17 08:41:06 +01:00
return $"{actionName} {binding.name}";
}
2026-06-17 08:41:06 +01:00
return actionName;
}
2026-06-17 08:41:06 +01:00
private static string DescribeInputPath(string inputPath)
{
2026-06-17 08:41:06 +01:00
if (string.IsNullOrWhiteSpace(inputPath))
{
2026-06-17 08:41:06 +01:00
return "Unbound";
}
2026-06-17 08:41:06 +01:00
var label = InputControlPath.ToHumanReadableString(
inputPath,
InputControlPath.HumanReadableStringOptions.OmitDevice);
return string.IsNullOrWhiteSpace(label) ? inputPath : label;
}
2026-06-17 08:41:06 +01:00
private string BuildCapturePrompt()
{
2026-06-17 08:41:06 +01:00
if (_captureBindingIndex < 0 || string.IsNullOrWhiteSpace(_captureActionName))
{
return "";
}
return $"Capture pending for {_captureBindingLabel}. Press a keyboard or mouse input now.";
}
2026-06-17 08:41:06 +01:00
private void HandleSurfaceCommand(EveSurfaceCommandRequest request)
{
2026-06-21 09:37:54 +01:00
if (!AetheriaRuntimeInputSettingsSurfaceCommands.TryRead(request, out var command))
{
Debug.LogWarning($"Unknown input-settings command: {request?.Command}");
return;
}
switch (command.Kind)
2026-06-17 08:41:06 +01:00
{
2026-06-21 09:37:54 +01:00
case AetheriaRuntimeInputSettingsCommandKind.Refresh:
2026-06-17 08:41:06 +01:00
RenderSurface();
return;
2026-06-21 09:37:54 +01:00
case AetheriaRuntimeInputSettingsCommandKind.CancelCapture:
2026-06-17 08:41:06 +01:00
ClearCapture();
RenderSurface();
return;
2026-06-21 09:37:54 +01:00
case AetheriaRuntimeInputSettingsCommandKind.BeginCapture:
BeginCapture(command);
2026-06-17 08:41:06 +01:00
return;
2026-06-21 09:37:54 +01:00
case AetheriaRuntimeInputSettingsCommandKind.ToggleActionBar:
ToggleActionBarInput(command);
2026-06-17 08:41:06 +01:00
return;
default:
2026-06-21 09:37:54 +01:00
Debug.LogWarning($"Unknown input-settings command: {request?.Command}");
2026-06-17 08:41:06 +01:00
return;
}
}
2026-06-21 09:37:54 +01:00
private void BeginCapture(AetheriaRuntimeInputSettingsSurfaceCommand command)
{
2026-06-21 09:37:54 +01:00
if (string.IsNullOrWhiteSpace(command.ActionName) || command.BindingIndex < 0)
2026-06-17 08:41:06 +01:00
{
Debug.LogWarning("Input capture requested without a valid action name and binding index.");
return;
}
2026-06-21 09:37:54 +01:00
_captureActionName = command.ActionName;
_captureBindingIndex = command.BindingIndex;
_captureBindingLabel = string.IsNullOrWhiteSpace(command.BindingLabel)
? command.ActionName
: command.BindingLabel;
2026-06-17 08:41:06 +01:00
_captureAction?.Enable();
RenderSurface();
}
2026-06-21 09:37:54 +01:00
private void ToggleActionBarInput(AetheriaRuntimeInputSettingsSurfaceCommand command)
{
2026-06-21 09:37:54 +01:00
if (string.IsNullOrWhiteSpace(command.InputPath))
{
2026-06-17 08:41:06 +01:00
Debug.LogWarning("Action-bar toggle requested without a valid input path and enabled state.");
return;
}
2026-06-25 18:09:33 +01:00
SendInputSettingsCommand(
AetheriaRuntimeEveCommandKind.SetActionBarEnabled,
new AetheriaRuntimeInputSettingsCommandBody
{
InputSystemPath = command.InputPath,
Enabled = command.Enabled
},
"action-bar input");
2026-06-17 08:41:06 +01:00
RenderSurface();
}
2026-06-17 08:41:06 +01:00
private void OnCapturePerformed(InputAction.CallbackContext context)
{
if (_captureBindingIndex < 0 || string.IsNullOrWhiteSpace(_captureActionName))
{
return;
}
2026-06-17 08:41:06 +01:00
var inputPath = context.control?.path ?? "";
if (string.IsNullOrWhiteSpace(inputPath) ||
inputPath.EndsWith("anyKey", StringComparison.Ordinal) ||
2026-06-21 21:20:16 +01:00
!AetheriaRuntimeInputSettingsSurfaceBuilder.IsSupportedCapturePath(inputPath) ||
2026-06-17 08:41:06 +01:00
Input == null)
{
return;
}
2026-06-13 04:56:50 +01:00
2026-06-17 08:41:06 +01:00
var action = Input.FindAction(_captureActionName, throwIfNotFound: false);
if (action == null || _captureBindingIndex >= action.bindings.Count)
{
2026-06-17 08:41:06 +01:00
Debug.LogWarning($"Input capture target no longer exists for {_captureActionName}:{_captureBindingIndex}.");
ClearCapture();
RenderSurface();
return;
}
2026-06-17 08:41:06 +01:00
action.ApplyBindingOverride(_captureBindingIndex, inputPath);
2026-06-25 18:09:33 +01:00
SendInputSettingsCommand(
AetheriaRuntimeEveCommandKind.SetBindingOverride,
new AetheriaRuntimeInputSettingsCommandBody
{
ActionName = action.name,
BindingIndex = _captureBindingIndex,
InputSystemPath = inputPath
},
"input binding override");
2026-06-17 08:41:06 +01:00
ClearCapture();
RenderSurface();
}
2026-06-25 18:09:33 +01:00
private void SendInputSettingsCommand(
AetheriaRuntimeEveCommandKind command,
AetheriaRuntimeInputSettingsCommandBody body,
string label)
{
try
{
var submitted = ResolveClient()
.Ui.InputSettingsAsync(command, body, "unity-input-screen")
.GetAwaiter()
.GetResult();
Debug.Log($"Submitted Aetheria {label} Eve operation: {submitted.OperationId}");
}
catch (Exception ex)
{
Debug.LogError($"Failed to send Aetheria {label} Eve command: {ex}");
}
}
private AetheriaClient ResolveClient()
{
var stateBoot = AetheriaRuntimeStateBoot.Inspect(AetheriaUnityRuntimePaths.GameDataDirectory);
2026-06-25 18:09:33 +01:00
if (!stateBoot.SupportsLocalStateFileRead || !stateBoot.StateFileExists)
{
throw new InvalidOperationException(
$"Input settings require a readable local Aetheria Verse state file: {stateBoot.FailureMessage}");
}
if (!string.Equals(_clientStatePath, stateBoot.StateFilePath, StringComparison.Ordinal))
2026-06-25 18:09:33 +01:00
{
_clientStatePath = stateBoot.StateFilePath;
ClearClientCaches();
2026-06-25 18:09:33 +01:00
}
return AetheriaUnityRuntimeClientProvider.ResolveClient(stateBoot, "unity-input-screen");
2026-06-25 18:09:33 +01:00
}
private void ClearClientCaches()
2026-06-25 18:09:33 +01:00
{
_playerSettings = null;
}
private AetheriaRuntimePlayerSettingsDocument ResolvePlayerSettings()
2026-06-25 18:09:33 +01:00
{
if (_playerSettings != null)
return _playerSettings;
try
{
_playerSettings = ResolveClient()
.Aetheria()
.Settings
.Player
.Latest();
2026-06-25 18:09:33 +01:00
}
catch (Exception ex)
{
Debug.LogWarning($"Failed to read Aetheria player settings for input screen: {ex.Message}");
}
return _playerSettings;
}
2026-06-17 08:41:06 +01:00
private void ClearCapture()
{
2026-06-17 08:41:06 +01:00
_captureActionName = "";
_captureBindingIndex = -1;
_captureBindingLabel = "";
}
}