using System; using System.Collections.Generic; using AcDream.Core.Items; using AcDream.Content; using DatReaderWriter; namespace AcDream.App.UI.Layout; /// /// Retail paperdoll slot definitions: one source of truth for the paperdoll element, /// accepted , and authored UIItem prototype whose /// ItemSlot_Empty state supplies that location's background. /// /// /// Element-to-location: gmPaperDollUI::GetLocationInfoFromElementID @ 0x004A37F0. /// Cell creation: UIElement_ItemList::InternalCreateItem @ 0x004E3570. /// Prototype media: live DAT catalog LayoutDesc 0x21000037. /// public static class PaperdollSlotBackgrounds { internal readonly record struct Definition( uint Element, EquipMask Mask, uint EmptyPrototype, AetheriaUnlockState UnlockBit = AetheriaUnlockState.None); // WEAPON_READY_SLOT_LOC (acclient.h:3235): any wieldable weapon in the hand slot. private const EquipMask WeaponSlotMask = EquipMask.MeleeWeapon | EquipMask.MissileWeapon | EquipMask.Held | EquipMask.TwoHanded; internal static readonly Definition[] Definitions = { new(0x100005ABu, EquipMask.HeadWear, 0x100005B4u), new(0x100001E2u, EquipMask.ChestWear, 0x1000044Eu), new(0x100001E3u, EquipMask.UpperLegWear, 0x1000044Fu), new(0x100005B0u, EquipMask.HandWear, 0x100005B9u), new(0x100005B3u, EquipMask.FootWear, 0x100005BDu), new(0x100005ACu, EquipMask.ChestArmor, 0x100005B5u), new(0x100005ADu, EquipMask.AbdomenArmor, 0x100005B6u), new(0x100005AEu, EquipMask.UpperArmArmor, 0x100005B7u), new(0x100005AFu, EquipMask.LowerArmArmor, 0x100005B8u), new(0x100005B1u, EquipMask.UpperLegArmor, 0x100005BAu), new(0x100005B2u, EquipMask.LowerLegArmor, 0x100005BBu), new(0x100001DAu, EquipMask.NeckWear, 0x10000446u), new(0x100001DBu, EquipMask.WristWearLeft, 0x10000447u), new(0x100001DDu, EquipMask.WristWearRight, 0x10000449u), new(0x100001DCu, EquipMask.FingerWearLeft, 0x10000448u), new(0x100001DEu, EquipMask.FingerWearRight, 0x1000044Au), new(0x100001E1u, EquipMask.Shield, 0x1000044Du), new(0x100001E0u, EquipMask.MissileAmmo, 0x1000044Cu), new(0x100001DFu, WeaponSlotMask, 0x1000044Bu), new(0x1000058Eu, EquipMask.TrinketOne, 0x1000058Fu), new(0x100005E9u, EquipMask.Cloak, 0x100005EAu), new(0x10000595u, EquipMask.SigilOne, 0x10000592u, AetheriaUnlockState.Blue), new(0x10000596u, EquipMask.SigilTwo, 0x10000593u, AetheriaUnlockState.Yellow), new(0x10000597u, EquipMask.SigilThree, 0x10000594u, AetheriaUnlockState.Red), }; /// Resolves every supported slot's exact empty RenderSurface from the live DAT. public static IReadOnlyDictionary ResolveEmptySprites(IDatReaderWriter dats) { var result = new Dictionary(Definitions.Length); foreach (Definition definition in Definitions) { uint sprite = ItemListCellTemplate.ResolvePrototypeEmptySprite( dats, definition.EmptyPrototype); if (sprite == 0) { throw new InvalidOperationException( $"Retail paperdoll UIItem prototype 0x{definition.EmptyPrototype:X8} " + $"for slot 0x{definition.Element:X8} has no ItemSlot_Empty surface."); } result.Add(definition.Element, sprite); } return result; } /// Exposes the authored prototype identity for dat-free conformance tests. public static bool TryGetPrototype(uint slotElementId, out uint prototypeElementId) { foreach (Definition definition in Definitions) { if (definition.Element != slotElementId) continue; prototypeElementId = definition.EmptyPrototype; return true; } prototypeElementId = 0; return false; } }