feat: port retail magic lifecycle and retained spell UI

Complete the retail cast-intent, target, component, enchantment, and busy-state paths; mount the DAT-authored spell bar, spellbook, component book, effects panels, and shared panel lifecycle; and add scoped input plus conformance coverage.

Co-Authored-By: Codex <noreply@openai.com>
This commit is contained in:
Erik 2026-07-15 10:55:22 +02:00
parent 7b7ffcd278
commit 07be994d97
84 changed files with 17822 additions and 1051 deletions

View file

@ -0,0 +1,144 @@
using System;
using System.Collections.Generic;
namespace AcDream.App.UI.Layout;
/// <summary>
/// Retained-window port of retail <c>gmPanelUI::RecvNotice_SetPanelVisibility</c>
/// at <c>0x004BC6F0</c>. The original owns one active child panel and an optional
/// deferred child; this owner applies the same lifecycle to registered retained
/// windows without making the toolbar authoritative for non-toolbar panels.
/// </summary>
public sealed class RetailPanelUiController
{
public const uint RestorePreviousPropertyId = 0x10000049u;
private readonly Func<string, bool> _isVisible;
private readonly Func<string, bool> _show;
private readonly Func<string, bool> _hide;
private readonly Dictionary<uint, PanelEntry> _byPanel = new();
private readonly Dictionary<string, uint> _byWindow = new(StringComparer.Ordinal);
private uint? _activePanel;
private uint? _deferredPanel;
private bool _applying;
public RetailPanelUiController(
Func<string, bool> isVisible,
Func<string, bool> show,
Func<string, bool> hide)
{
_isVisible = isVisible ?? throw new ArgumentNullException(nameof(isVisible));
_show = show ?? throw new ArgumentNullException(nameof(show));
_hide = hide ?? throw new ArgumentNullException(nameof(hide));
}
public uint? ActivePanelId => _activePanel;
/// <param name="restorePrevious">
/// Effective DAT bool property <c>0x10000049</c>. Retail retains the previous
/// panel only when the newly shown panel has this property and the previous
/// panel does not.
/// </param>
public void Register(uint panelId, string windowName, bool restorePrevious = false)
{
if (panelId == 0) throw new ArgumentOutOfRangeException(nameof(panelId));
ArgumentException.ThrowIfNullOrWhiteSpace(windowName);
if (_byPanel.ContainsKey(panelId) || _byWindow.ContainsKey(windowName))
throw new InvalidOperationException(
$"Panel {panelId} or retained window '{windowName}' is already registered.");
_byPanel.Add(panelId, new PanelEntry(windowName, restorePrevious));
_byWindow.Add(windowName, panelId);
if (_isVisible(windowName))
ObserveWindowVisibility(windowName, visible: true);
}
/// <summary>
/// Registers a panel directly from its fully inherited retail root so the
/// authored deferred-panel behavior cannot be dropped at the mount seam.
/// </summary>
public void Register(uint panelId, string windowName, ElementInfo authoredRoot)
{
ArgumentNullException.ThrowIfNull(authoredRoot);
Register(
panelId,
windowName,
authoredRoot.TryGetEffectiveBool(
RestorePreviousPropertyId,
out bool restorePrevious)
&& restorePrevious);
}
public bool IsPanelVisible(uint panelId)
=> _byPanel.TryGetValue(panelId, out PanelEntry entry)
&& _isVisible(entry.WindowName);
public bool TogglePanel(uint panelId)
{
bool visible = !IsPanelVisible(panelId);
return SetPanelVisibility(panelId, visible) && visible;
}
public bool SetPanelVisibility(uint panelId, bool visible)
{
if (!_byPanel.TryGetValue(panelId, out PanelEntry requested)) return false;
_applying = true;
try
{
if (visible)
{
if (_activePanel == panelId)
return _show(requested.WindowName);
uint? previousId = _activePanel;
PanelEntry? previous = previousId is uint id
&& _byPanel.TryGetValue(id, out PanelEntry found)
&& _isVisible(found.WindowName)
? found
: null;
_deferredPanel = requested.RestorePrevious
&& previous is { RestorePrevious: false }
? previousId
: null;
_activePanel = panelId;
if (previous is not null)
_hide(previous.Value.WindowName);
return _show(requested.WindowName);
}
if (_activePanel != panelId)
{
if (_deferredPanel == panelId) _deferredPanel = null;
return _hide(requested.WindowName);
}
bool hidden = _hide(requested.WindowName);
_activePanel = null;
if (_deferredPanel is not uint deferredId
|| !_byPanel.TryGetValue(deferredId, out PanelEntry deferred))
return hidden;
_deferredPanel = null;
_activePanel = deferredId;
return _show(deferred.WindowName) || hidden;
}
finally
{
_applying = false;
}
}
/// <summary>
/// Feeds visibility changes made by persistence or another retained-window
/// owner back through the canonical panel lifecycle.
/// </summary>
public void ObserveWindowVisibility(string windowName, bool visible)
{
if (_applying || !_byWindow.TryGetValue(windowName, out uint panelId)) return;
SetPanelVisibility(panelId, visible);
}
private readonly record struct PanelEntry(string WindowName, bool RestorePrevious);
}