fix #212: preserve shortcut tint in physical combat

Port the retail m_bShortcutGhosted meaning instead of treating SetShortcutNum's boolean as peace versus war. Keep occupied shortcut numbers on the gold sheet in NonCombat, Melee, and Missile, reserve the gray sheet for Magic, and lock the four-mode matrix with conformance tests and corrected research.

Co-Authored-By: Codex <codex@openai.com>
This commit is contained in:
Erik 2026-07-13 11:21:12 +02:00
parent ab98cda26b
commit 5090aa7217
9 changed files with 197 additions and 112 deletions

View file

@ -40,7 +40,7 @@ Plus a special case at `407546` (`0058d1ee`): **`IsThePlayer`** → `m_idIcon =
- **`src/AcDream.Core/Items/ItemInstance.cs`** — has `IconId`, `IconUnderlayId`, `IconOverlayId`, `Type`. **No `Effects`/`UiEffects` field yet.**
- **`src/AcDream.Core/Items/ItemRepository.cs`** — `EnrichItem(objectId, iconId, name, type, iconOverlayId=0, iconUnderlayId=0)` writes the typed icon ids onto an existing item + fires `ItemPropertiesUpdated`. Threaded from `WorldSession.EntitySpawned``GameWindow.OnLiveEntitySpawned`.
- **`src/AcDream.App/UI/Layout/ToolbarController.cs`** — calls `iconIds(item.Type, item.IconId, item.IconUnderlayId, item.IconOverlayId)` per slot, re-runs `Populate()` on `ItemRepository.ItemAdded`/`ItemPropertiesUpdated` (so a late `CreateObject` re-binds the slot's icon).
- **(related, not icon-composite)** the **slot-number** system (`SetShortcutNum`, 3 digit arrays: occupied peace/war `0x10000042`/`0x10000043` from cell composite `0x10000346`, empty/background `0x1000005e` from composite `0x10000341`) is done — it's a separate `UIElement_UIItem` feature, not the icon composite, but lives on the same widget.
- **(related, not icon-composite)** the **slot-number** system (`SetShortcutNum`, 3 digit arrays: occupied regular/ghosted `0x10000042`/`0x10000043` from cell composite `0x10000346`, empty/background `0x1000005e` from composite `0x10000341`) is done — it's a separate `UIElement_UIItem` feature, not the icon composite, but lives on the same widget. The Boolean is `m_bShortcutGhosted`, true only in Magic mode; it is not peace/war.
---

View file

@ -224,6 +224,45 @@ parse/store/write even though this toolbar ignores it. Do not add spell icons or
spell casting to `gmToolbarUI`; implement the eight favorite-spell lists as a
separate subsystem.
### 2.4 Physical-shortcut number ghosting
The Boolean parameter of `UIElement_UIItem::SetShortcutNum @ 0x004E1590` is
not a peace/war selector. `ACCWeenieObject::SetShortcutNum @ 0x0058C100`
stores it verbatim as `m_bShortcutGhosted`, and `UIElement_UIItem::Update`
replays that named field into the widget. The digit-array selection is:
```text
UIElement_UIItem.SetShortcutNum(slot, ghosted): // 0x004E1590
if slot < 0:
hide shortcut-number element
return
if item icon is in empty state 0x1000001C:
digits = property 0x1000005E // empty slot
else if ghosted:
digits = property 0x10000043 // gray mesh
else:
digits = property 0x10000042 // gold mesh
set number image to digits[slot]
show shortcut-number element
```
`gmToolbarUI::RecvNotice_SetCombatMode @ 0x004BD610` first stores
`mode != MAGIC_COMBAT_MODE`, then passes the inverse to every occupied
shortcut cell. In direct pseudocode:
```text
shortcutsGhosted = (mode == MAGIC_COMBAT_MODE)
for each occupied shortcut slot i:
cell.SetShortcutNum(i, shortcutsGhosted)
```
Therefore NonCombat, Melee, and Missile all use property `0x10000042`; only
Magic uses the ghosted `0x10000043` sheet. Calling these arrays “peace” and
“war” reverses the meaning and incorrectly grays physical shortcut numbers in
melee and missile combat.
---
## 3. `gmToolbarUI` input, use, and selection pseudocode