fix(D.5.1): occupancy-gated slot numbers (empty=0x1000005e bg digit) + bottom-right rect probe
FIX 1: UIElement_UIItem::SetShortcutNum (decomp 229481) has a three-way source branch: occupied+peace -> 0x10000042 (peace digit set), occupied+war -> 0x10000043 (war digit set), empty (ItemId==0) -> 0x1000005e (background digit, stance-independent). acdream previously only had the peace/war pair and drew them regardless of occupancy. Changes: - GameWindow.cs: read property 0x1000005e into toolbarEmptyDigits (no fallback; null is safe). Logs entry count. Passes emptyDigits to Bind. Adds [D.5.1 probe] block logging screen pos + size of 7 bottom-right element ids via ScreenPosition. - ToolbarController.cs: adds _emptyDigits field, emptyDigits ctor+Bind param (null default). RestampShortcutNumbers sets cell.EmptyDigits. Comments cite decomp 229481. - UiItemSlot.cs: adds EmptyDigits property + ActiveDigitArray() internal testable seam (occupied -> peace/war by stance; empty -> EmptyDigits). OnDraw uses it. Comment updated with three-way source table. - Tests: 5 new UiItemSlotTests (ActiveDigitArray occupancy), 2 new ToolbarControllerTests (emptyDigits injection + null-safe). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
7d5a88cd15
commit
a7cad5566b
5 changed files with 200 additions and 24 deletions
|
|
@ -52,12 +52,20 @@ public sealed class UiItemSlot : UiElement
|
|||
public bool ShortcutPeace { get; private set; } = true;
|
||||
|
||||
/// <summary>Peace digit DID array. Index i → digit (i+1) sprite RenderSurface id.
|
||||
/// Injected by the controller after reading LayoutDesc 0x21000037.</summary>
|
||||
/// Injected by the controller after reading LayoutDesc 0x21000037.
|
||||
/// Retail ref: UIElement_UIItem::SetShortcutNum (decomp 229481) — occupied slot picks
|
||||
/// property 0x10000042 (peace) or 0x10000043 (war) by stance.</summary>
|
||||
public uint[]? PeaceDigits { get; set; }
|
||||
|
||||
/// <summary>War digit DID array. Same layout as PeaceDigits.</summary>
|
||||
/// <summary>War digit DID array. Same layout as PeaceDigits.
|
||||
/// Retail ref: UIElement_UIItem::SetShortcutNum (decomp 229493) — war stance.</summary>
|
||||
public uint[]? WarDigits { get; set; }
|
||||
|
||||
/// <summary>Empty-slot digit DID array (property 0x1000005e, stance-independent).
|
||||
/// Used when the slot is EMPTY (ItemId == 0). Retail ref: UIElement_UIItem::SetShortcutNum
|
||||
/// (decomp 229481) — else branch when m_elem_Icon->m_state == 0x1000001c (empty).</summary>
|
||||
public uint[]? EmptyDigits { get; set; }
|
||||
|
||||
/// <summary>Set the slot's shortcut position and combat stance so the correct digit
|
||||
/// is drawn. Call with index 0..8 for the top row; pass peace=true for NonCombat.</summary>
|
||||
public void SetShortcutNum(int index, bool peace)
|
||||
|
|
@ -69,6 +77,20 @@ public sealed class UiItemSlot : UiElement
|
|||
/// <summary>Clear the shortcut number label (hides the digit).</summary>
|
||||
public void ClearShortcutNum() { ShortcutNum = -1; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the digit DID array that OnDraw will use, following the retail occupancy
|
||||
/// branch in UIElement_UIItem::SetShortcutNum (decomp 229481):
|
||||
/// occupied (ItemId != 0) → ShortcutPeace ? PeaceDigits : WarDigits (0x10000042/43)
|
||||
/// empty (ItemId == 0) → EmptyDigits (0x1000005e, stance-independent)
|
||||
/// Exposed as an internal method so unit tests can assert array selection without
|
||||
/// needing a real render context.
|
||||
/// </summary>
|
||||
internal uint[]? ActiveDigitArray()
|
||||
{
|
||||
bool occupied = ItemId != 0;
|
||||
return occupied ? (ShortcutPeace ? PeaceDigits : WarDigits) : EmptyDigits;
|
||||
}
|
||||
|
||||
// ── Events / draw ─────────────────────────────────────────────────────────
|
||||
|
||||
/// <summary>Invoked by <see cref="OnEvent"/> when a left-button-down lands on
|
||||
|
|
@ -84,10 +106,8 @@ public sealed class UiItemSlot : UiElement
|
|||
|
||||
protected override void OnDraw(UiRenderContext ctx)
|
||||
{
|
||||
// Draw the icon (filled slot) or the empty-slot border. Both paths fall
|
||||
// through to the digit draw below, so the slot label shows on all top-row
|
||||
// slots regardless of whether they hold an item (retail screenshot confirms
|
||||
// numbers on empty slots).
|
||||
// Draw the icon (filled slot) or the empty-slot border. Both paths fall through
|
||||
// to the digit draw below; the slot label always shows on top-row slots.
|
||||
if (ItemId != 0 && IconTexture != 0)
|
||||
{
|
||||
ctx.DrawSprite(IconTexture, 0f, 0f, Width, Height, 0f, 0f, 1f, 1f, Vector4.One);
|
||||
|
|
@ -100,11 +120,14 @@ public sealed class UiItemSlot : UiElement
|
|||
}
|
||||
|
||||
// Digit overlay: UIElement_UIItem::SetShortcutNum (acclient_2013_pseudo_c.txt:229465).
|
||||
// Each digit image is corner-baked (glyph in top-left, rest alpha=0) so we
|
||||
// draw it full-cell size and the transparent region is invisible. DrawMode=Alphablend.
|
||||
// Occupancy branch (decomp 229481):
|
||||
// occupied (ItemId != 0) → peace/war digit set 0x10000042/43, split by stance
|
||||
// empty (ItemId == 0) → background digit set 0x1000005e, stance-independent
|
||||
// Each digit image is corner-baked (glyph in top-left, rest alpha=0); drawn
|
||||
// full-cell Alphablend so the transparent region is invisible.
|
||||
if (ShortcutNum >= 0 && SpriteResolve is not null)
|
||||
{
|
||||
var arr = ShortcutPeace ? PeaceDigits : WarDigits;
|
||||
var arr = ActiveDigitArray();
|
||||
if (arr is not null && ShortcutNum < arr.Length)
|
||||
{
|
||||
uint did = arr[ShortcutNum];
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue