using System; using System.IO; using AcDream.App.UI; using AcDream.App.UI.Layout; using DatReaderWriter; using DatReaderWriter.Options; using Xunit; namespace AcDream.App.Tests.UI.Layout; /// /// Real-dat smoke test for the gmInventoryUI frame (0x21000023) import + the sub-window /// mount. Locks the #145-continuation fix: the mount keeps each mounted panel slot's OWN /// frame ZLevel instead of inheriting the sub-window root's ZLevel 1000. Before the fix the /// merge's zero-wins-base rule gave the slots ZLevel 1000 → the #145 ZOrder fold /// (ReadOrder − ZLevel·10000) sank them to ≈ −10,000,000, BEHIND the frame's Alphablend /// backdrop (ZLevel 100 → ≈ −1,000,000), and the backdrop washed out the panels' captions, /// burden meter, and item cells. Skips when the live dat directory is absent (CI). /// public class InventoryFrameImportProbe { private const uint Frame = 0x21000023u; private const uint Backdrop = 0x100001D0u; // full-window Alphablend backdrop (ZLevel 100) private const uint BackpackPanel = 0x100001CEu; // mounted gmBackpackUI slot private const uint ItemsPanel = 0x100001CFu; // mounted gm3DItemsUI slot private const uint PaperdollPanel = 0x100001CDu; // mounted gmPaperDollUI slot private const uint BurdenMeter = 0x100001D9u; // backpack content (proves the mount attached it) private const uint ContentsGrid = 0x100001C6u; // 3D-items content private static string? DatDir() { var d = Environment.GetEnvironmentVariable("ACDREAM_DAT_DIR") ?? Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "Documents", "Asheron's Call"); return Directory.Exists(d) ? d : null; } [Fact] public void Paperdoll_equip_slots_resolve_to_item_lists() { var datDir = DatDir(); if (datDir is null) return; // CI: no live dat — skip using var dats = new DatCollection(datDir, DatAccessType.Read); var layout = LayoutImporter.Import(dats, Frame, _ => (0u, 0, 0), null); Assert.NotNull(layout); // A representative spread across the slot grid (head, shield, the weapon composite, cloak, // trinket, a finger). All inherit base 0x100001E4 → 0x2100003D (Type 0x10000031 = UIElement_ItemList). foreach (uint id in new[] { 0x100005ABu, 0x100001E1u, 0x100001DFu, 0x100005E9u, 0x1000058Eu, 0x100001DCu }) { var el = layout!.FindElement(id); Assert.True(el is UiItemList, $"equip slot 0x{id:X8} resolved to {el?.GetType().Name ?? "null"}, expected UiItemList"); } } [Fact] public void Mounted_panels_sit_in_front_of_the_backdrop() { var datDir = DatDir(); if (datDir is null) return; // CI: no live dat — skip (this is a smoke test) using var dats = new DatCollection(datDir, DatAccessType.Read); var layout = LayoutImporter.Import(dats, Frame, _ => (0u, 0, 0), null); Assert.NotNull(layout); var backdrop = layout!.FindElement(Backdrop); Assert.NotNull(backdrop); // the full-window backdrop is a direct child of the frame // The sub-window mount brings each panel's content into the by-id index. Assert.NotNull(layout.FindElement(BurdenMeter)); // backpack burden meter Assert.NotNull(layout.FindElement(ContentsGrid)); // 3D-items contents grid // The fix: every mounted panel slot must draw IN FRONT of the backdrop // (higher ZOrder = painted later = on top), so the backdrop sits behind the content. foreach (var (id, name) in new[] { (BackpackPanel, "backpack"), (ItemsPanel, "3D-items"), (PaperdollPanel, "paperdoll"), }) { var panel = layout.FindElement(id); Assert.True(panel is not null, $"{name} panel 0x{id:X8} missing from the imported tree"); Assert.True(panel!.ZOrder > backdrop!.ZOrder, $"{name} panel ZOrder {panel.ZOrder} must be > backdrop ZOrder {backdrop.ZOrder} " + "(else the Alphablend backdrop overpaints/washes out the panel content)"); } } }