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
|
|
@ -171,9 +171,11 @@ public class ToolbarControllerTests
|
|||
// Port of UIElement_UIItem::SetShortcutNum (acclient_2013_pseudo_c.txt:229465);
|
||||
// gmToolbarUI::RecvNotice_SetCombatMode (196610-196621).
|
||||
|
||||
// Fake digit arrays: 9 peace entries (0x10..0x18), 9 war entries (0x20..0x28).
|
||||
// Fake digit arrays: 9 peace entries (0x10..0x18), 9 war entries (0x20..0x28),
|
||||
// 9 empty (background) entries (0x30..0x38).
|
||||
private static readonly uint[] FakePeace = { 0x10u,0x11u,0x12u,0x13u,0x14u,0x15u,0x16u,0x17u,0x18u };
|
||||
private static readonly uint[] FakeWar = { 0x20u,0x21u,0x22u,0x23u,0x24u,0x25u,0x26u,0x27u,0x28u };
|
||||
private static readonly uint[] FakeEmpty = { 0x30u,0x31u,0x32u,0x33u,0x34u,0x35u,0x36u,0x37u,0x38u };
|
||||
|
||||
/// <summary>
|
||||
/// After Bind with peace/war digit arrays, top-row cells (indices 0–8) have
|
||||
|
|
@ -272,4 +274,44 @@ public class ToolbarControllerTests
|
|||
Assert.Same(FakeWar, slots[id].Cell.WarDigits);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// EmptyDigits (0x1000005e background digit) is injected into every slot cell.
|
||||
/// Retail ref: UIElement_UIItem::SetShortcutNum (decomp 229481) — empty-slot branch.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void ShortcutNumbers_emptyDigitArrayInjected()
|
||||
{
|
||||
var (layout, slots, _) = FakeToolbar();
|
||||
var repo = new ItemRepository();
|
||||
|
||||
ToolbarController.Bind(layout, repo,
|
||||
() => Array.Empty<PlayerDescriptionParser.ShortcutEntry>(),
|
||||
iconIds: (_,_,_,_) => 0u, useItem: _ => { },
|
||||
peaceDigits: FakePeace, warDigits: FakeWar, emptyDigits: FakeEmpty);
|
||||
|
||||
foreach (var id in Row1)
|
||||
Assert.Same(FakeEmpty, slots[id].Cell.EmptyDigits);
|
||||
foreach (var id in Row2)
|
||||
Assert.Same(FakeEmpty, slots[id].Cell.EmptyDigits);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When emptyDigits is null, cells have EmptyDigits == null (no digit on empty slots).
|
||||
/// This is the safe fallback when the dat property 0x1000005e is absent.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void ShortcutNumbers_nullEmptyDigits_cellsHaveNullEmptyDigits()
|
||||
{
|
||||
var (layout, slots, _) = FakeToolbar();
|
||||
var repo = new ItemRepository();
|
||||
|
||||
ToolbarController.Bind(layout, repo,
|
||||
() => Array.Empty<PlayerDescriptionParser.ShortcutEntry>(),
|
||||
iconIds: (_,_,_,_) => 0u, useItem: _ => { },
|
||||
peaceDigits: FakePeace, warDigits: FakeWar, emptyDigits: null);
|
||||
|
||||
foreach (var id in Row1)
|
||||
Assert.Null(slots[id].Cell.EmptyDigits);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -82,4 +82,63 @@ public class UiItemSlotTests
|
|||
s.ClearShortcutNum();
|
||||
Assert.Equal(-1, s.ShortcutNum);
|
||||
}
|
||||
|
||||
// ── ActiveDigitArray occupancy gating (decomp UIElement_UIItem::SetShortcutNum:229481) ──
|
||||
|
||||
private static readonly uint[] Peace = { 0x10u, 0x11u, 0x12u };
|
||||
private static readonly uint[] War = { 0x20u, 0x21u, 0x22u };
|
||||
private static readonly uint[] Empty = { 0x30u, 0x31u, 0x32u };
|
||||
|
||||
/// <summary>
|
||||
/// When ItemId == 0 (empty slot), ActiveDigitArray returns EmptyDigits regardless
|
||||
/// of ShortcutPeace. Retail ref: UIElement_UIItem::SetShortcutNum (decomp 229481) —
|
||||
/// else branch when m_elem_Icon->m_state == 0x1000001c (empty).
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void ActiveDigitArray_emptySlot_returnsEmptyDigits()
|
||||
{
|
||||
var s = new UiItemSlot { PeaceDigits = Peace, WarDigits = War, EmptyDigits = Empty };
|
||||
s.SetShortcutNum(0, peace: true);
|
||||
// ItemId == 0 → EmptyDigits
|
||||
Assert.Same(Empty, s.ActiveDigitArray());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ActiveDigitArray_emptySlot_warStance_stillReturnsEmptyDigits()
|
||||
{
|
||||
var s = new UiItemSlot { PeaceDigits = Peace, WarDigits = War, EmptyDigits = Empty };
|
||||
s.SetShortcutNum(0, peace: false);
|
||||
// ItemId == 0 → EmptyDigits regardless of stance
|
||||
Assert.Same(Empty, s.ActiveDigitArray());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When ItemId != 0 (occupied), ActiveDigitArray returns PeaceDigits or WarDigits
|
||||
/// depending on ShortcutPeace. Retail ref: UIElement_UIItem::SetShortcutNum (decomp 229481/229493).
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void ActiveDigitArray_occupiedSlot_peaceStance_returnsPeaceDigits()
|
||||
{
|
||||
var s = new UiItemSlot { PeaceDigits = Peace, WarDigits = War, EmptyDigits = Empty };
|
||||
s.SetItem(0x5001u, 0x99u);
|
||||
s.SetShortcutNum(0, peace: true);
|
||||
Assert.Same(Peace, s.ActiveDigitArray());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ActiveDigitArray_occupiedSlot_warStance_returnsWarDigits()
|
||||
{
|
||||
var s = new UiItemSlot { PeaceDigits = Peace, WarDigits = War, EmptyDigits = Empty };
|
||||
s.SetItem(0x5001u, 0x99u);
|
||||
s.SetShortcutNum(0, peace: false);
|
||||
Assert.Same(War, s.ActiveDigitArray());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ActiveDigitArray_emptySlot_nullEmptyDigits_returnsNull()
|
||||
{
|
||||
var s = new UiItemSlot { PeaceDigits = Peace, WarDigits = War, EmptyDigits = null };
|
||||
s.SetShortcutNum(0, peace: true);
|
||||
Assert.Null(s.ActiveDigitArray());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue