fix(paperdoll): restore authored slot backgrounds
Port the retail UIItem catalog path so every supported equipment location resolves its exact empty-state silhouette from live DAT. Share one definition table between equip behavior and presentation, wire runtime plus UI Studio, pin all 21 prototypes and surfaces, and retire AP-66. Co-Authored-By: Codex <codex@openai.com>
This commit is contained in:
parent
6ba4148b24
commit
1be18cc4db
11 changed files with 335 additions and 52 deletions
86
src/AcDream.App/UI/Layout/PaperdollSlotBackgrounds.cs
Normal file
86
src/AcDream.App/UI/Layout/PaperdollSlotBackgrounds.cs
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using AcDream.Core.Items;
|
||||
using DatReaderWriter;
|
||||
|
||||
namespace AcDream.App.UI.Layout;
|
||||
|
||||
/// <summary>
|
||||
/// Retail paperdoll slot definitions: one source of truth for the paperdoll element,
|
||||
/// accepted <see cref="EquipMask"/>, and authored UIItem prototype whose
|
||||
/// <c>ItemSlot_Empty</c> state supplies that location's background.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Element-to-location: <c>gmPaperDollUI::GetLocationInfoFromElementID @ 0x004A37F0</c>.
|
||||
/// Cell creation: <c>UIElement_ItemList::InternalCreateItem @ 0x004E3570</c>.
|
||||
/// Prototype media: live DAT catalog <c>LayoutDesc 0x21000037</c>.
|
||||
/// </remarks>
|
||||
public static class PaperdollSlotBackgrounds
|
||||
{
|
||||
internal readonly record struct Definition(
|
||||
uint Element,
|
||||
EquipMask Mask,
|
||||
uint EmptyPrototype);
|
||||
|
||||
// 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),
|
||||
};
|
||||
|
||||
/// <summary>Resolves every supported slot's exact empty RenderSurface from the live DAT.</summary>
|
||||
public static IReadOnlyDictionary<uint, uint> ResolveEmptySprites(DatCollection dats)
|
||||
{
|
||||
var result = new Dictionary<uint, uint>(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;
|
||||
}
|
||||
|
||||
/// <summary>Exposes the authored prototype identity for dat-free conformance tests.</summary>
|
||||
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;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue