acdream/src/AcDream.App/UI/Layout/SpellbookRowStyle.cs
Erik 749e8ceeb1 fix(rendering): bound portal resource lifetime
Separate logical ownership, render publication, and GPU retirement across live entities, landblocks, particles, textures, mesh arenas, portal/UI teardown, and per-frame scratch storage. Add bounded DAT/texture caches, upload budgets, three-frame fence retirement, exact-incarnation appearance reconciliation, frame pacing, and extensive lifetime conformance coverage.\n\nThe seven-destination connected route now cuts peak working/private memory roughly in half, returns Caul to 125-153 FPS locally, and produces no WER or AMD reset.\n\nCo-authored-by: OpenAI Codex <codex@openai.com>
2026-07-18 21:35:16 +02:00

73 lines
2.4 KiB
C#

using System.Numerics;
using AcDream.Content;
using DatReaderWriter;
namespace AcDream.App.UI.Layout;
/// <summary>
/// Resolved presentation of retail's spell-shortcut row prototype. The spellbook
/// list points at this prototype through ItemList attribute <c>0x1000000E</c>;
/// retail clones it for every learned spell.
/// </summary>
public readonly record struct SpellbookRowStyle(
float Width,
float Height,
float IconLeft,
float IconTop,
float IconWidth,
float IconHeight,
float LabelLeft,
float LabelWidth,
uint FontDid,
Vector4 LabelColor,
uint BackgroundSprite,
uint SelectedSprite)
{
public const uint CatalogLayoutId = 0x21000037u;
public const uint PrototypeId = 0x10000343u;
public const uint SelectedOverlayId = 0x10000342u;
public const uint LabelId = 0x10000344u;
public const uint IconId = 0x1000033Bu;
/// <summary>
/// Resolve the complete row geometry and media from the installed retail DAT.
/// Returns null if the authored prototype is incomplete; callers then leave the
/// panel unbound rather than inventing replacement art.
/// </summary>
public static SpellbookRowStyle? TryLoad(IDatReaderWriter dats)
{
ArgumentNullException.ThrowIfNull(dats);
ElementInfo? prototype = LayoutImporter.ImportInfos(dats, CatalogLayoutId, PrototypeId);
if (prototype is null
|| Find(prototype, SelectedOverlayId) is not { } selected
|| Find(prototype, LabelId) is not { } label
|| Find(prototype, IconId) is not { } icon
|| !prototype.StateMedia.TryGetValue("", out var background)
|| !selected.StateMedia.TryGetValue("", out var selection)
|| prototype.Width <= 0f
|| prototype.Height <= 0f)
return null;
return new SpellbookRowStyle(
prototype.Width,
prototype.Height,
icon.X,
icon.Y,
icon.Width,
icon.Height,
label.X,
label.Width,
label.FontDid,
label.FontColor ?? Vector4.One,
background.File,
selection.File);
}
private static ElementInfo? Find(ElementInfo root, uint id)
{
if (root.Id == id) return root;
foreach (ElementInfo child in root.Children)
if (Find(child, id) is { } found) return found;
return null;
}
}