feat(ui): port retail toolbar ammo count
Resolve stacked thrown weapons versus separately equipped missile ammo exactly like gmToolbarUI, normalize a zero stack to one, and refresh the authored missile indicator from equipment and stack events. Keep the decision in pure Core with retained and real-DAT conformance coverage. Co-Authored-By: Codex <codex@openai.com>
This commit is contained in:
parent
400f01eef1
commit
564d39dfea
9 changed files with 330 additions and 7 deletions
|
|
@ -203,7 +203,7 @@ AP-94..AP-110 for the confirmed retail-UI completion gaps.
|
|||
| ~~AP-97~~ | **RETIRED 2026-07-10 (Wave 1 retained-widget foundation)** — the prior generic Device-timer premise was a decomp misread. Named retail polls the hovered element's tooltip deadline, then broadcasts global UI time message `3`; it does not expose the assumed arbitrary timer queue. `UiRoot.Tick` now preserves that order and subtree removal clears input/time ownership before another pulse. | `src/AcDream.App/UI/UiRoot.cs`; `IUiGlobalTimeListener.cs` | — | — | `UIElementManager::UseTime` and hover/focus paths, pinned in `docs/research/2026-07-10-retained-widget-foundations-pseudocode.md` |
|
||||
| ~~AP-98~~ | **RETIRED 2026-07-10 (Wave 2.3)** — typed handles/manager own lifecycle; every production/Studio window uses `RetailWindowFrame`; schema-v2 `UiWindowLayout` persists outer bounds, visibility, toolbar collapse, and chat maximize by character/resolution with legacy-radar migration, nearest-resolution fallback, clamping, and a pre-login `default` write guard. Chat maximize uses DAT 100–360 limits and preserves its lower edge when growing upward. | `src/AcDream.App/UI/RetailWindowManager.cs`; `RetailWindowLayoutPersistence.cs`; `RetailWindowHandle.cs`; `Layout/RetailWindowFrame.cs`; `ChatWindowController.cs`; `SettingsStore.cs` | — | — | `gmMainChatUI::HandleMaximizeButton @ 0x004CCE50`; retail `saveui/loadui` behavior |
|
||||
| ~~AP-99~~ | **RETIRED 2026-07-11 (Wave 3.2)** — Core `ItemInteractionPolicy` ports the complete ordered `DetermineUseResult`/`UseObject`/`AttemptPlaceIn3D` matrix; PWD flags and `CombatUse` survive CreateObject; component-pack membership comes from portal.dat; App owns throttle, UseDone-balanced busy state, target mode, wire/optimistic dispatch, and typed confirmation/auxiliary seams. The two corrupt decompiler operands were pinned from matching x86 as `BF_REQUIRES_PACKSLOT` and `BF_VENDOR`. | `src/AcDream.Core/Items/ItemInteractionPolicy.cs`; `src/AcDream.App/UI/ItemInteractionController.cs` | — | — | `ItemHolder::DetermineUseResult @ 0x00588460`; `UseObject @ 0x00588A80`; `AttemptPlaceIn3D @ 0x00588600` |
|
||||
| AP-101 | Toolbar ammo-number updates are not wired | `src/AcDream.App/UI/Layout/ToolbarController.cs` | All seven DAT-discovered panel launchers, combat, Use, Examine, selected health/mana/name/stack controls, and shortcut actions work; unavailable panels are ghosted and mounted-panel highlights follow window lifecycle events | Missile combat does not display the equipped ammunition count | `gmToolbarUI::UpdateAmmoNumber`; `gmToolbarUI @ 0x004BD0C0..0x004BF380`; LayoutDesc `0x21000016` |
|
||||
| ~~AP-101~~ | **RETIRED 2026-07-11 (Wave 4.4e)** — toolbar control coverage is complete: DAT panel launchers, combat, Use/Examine, selected-object strip, shortcuts, and exact thrown-weapon/separate-ammo count resolution on authored element `0x10000194`. | `src/AcDream.Core/Items/ToolbarAmmoPolicy.cs`; `src/AcDream.App/UI/Layout/ToolbarController.cs` | — | — | `gmToolbarUI::UpdateAmmoID @ 0x004BF210`; `UpdateAmmoNumber @ 0x004BE9E0` |
|
||||
| AP-104 | Vitals detail element `0x100004A9` and root `HideDetail`/`ShowDetail` transitions are not wired | `src/AcDream.App/UI/Layout/VitalsController.cs` | Compact vitals values/bars are correct | Detail click does nothing and expanded retail state is unreachable | `gmVitalsUI::ListenToElementMessage @ 0x004BFC00`; `PostInit @ 0x004BFCE0` |
|
||||
| AP-105 | **PARTIAL 2026-07-10 (Wave 2.2)** — inherited scrollbar media/roles now come from DAT (decrement/top `0x06004C69`, increment/bottom `0x06004C6C`). Retained chat still lacks complete tab/filter/unread, social availability, squelch, and focus-opacity behavior; a second ChatVM still loses FPS/position providers. | `src/AcDream.App/UI/Layout/DatWidgetFactory.cs`; `ChatWindowController.cs`; chat mount in `GameWindow.cs` | Shared log/send path, wrapping, scrollbar roles, and outer maximize geometry work; Wave 5 consolidates remaining chat state | Commands degrade, tabs are no-ops, moderation/channel state is wrong, and focus visuals diverge | `gmMainChatUI @ 0x004CCCC0..0x004CE2A0`; `UIElement_Scrollbar::OnSetAttribute @ 0x004714D0`; `ChatInterface` methods |
|
||||
| AP-106 | The retained UI has no external/ground-container window lifecycle, while the original owned-side-bag `NoLongerViewingContents` premise was incorrect (#196) | `src/AcDream.App/UI`; window runtime; absent external-container controller | Owned inventory navigation remains usable and must not gain an unproven `0x0195` send; Wave 2/6 adds lifecycle ownership and the separate external surface | External container replacement/close cannot notify the server exactly once; adding the packet to owned bag close would itself diverge from the named retail call graph | `ClientUISystem.groundObject`; `CM_Inventory::Event_NoLongerViewingContents @ 0x006ABC50`; `docs/research/2026-07-10-retail-panel-behavior-pseudocode.md` |
|
||||
|
|
|
|||
|
|
@ -494,6 +494,7 @@ behavior. Estimated 17–26 days focused work, 3–5 weeks calendar.
|
|||
- **✓ SHIPPED — Wave 4.4b selected-item mana.** Owned non-stack selections send exact `QueryItemMana 0x0263`; `0x0264` parses guid/fraction/trailing-valid into Core `ItemManaState`; valid matching responses reveal authored meter `0x100001A2`, invalid and visible selection-change paths cancel with guid zero. Health uses the same retail visible-meter cancellation rule. Wire/state/controller conformance is green in the 4,712-test Release suite. Live Mana Stone source-destruction → armor-mana-transfer → bar-update gate passed 2026-07-11. AP-101 now covers remaining toolbar controls only.
|
||||
- **✓ SHIPPED — Wave 4.4c exact selected-health policy.** Core ports `ObjectIsAttackable @ 0x0056A600`; CreateObject preserves second-header PetOwner; toolbar player/pet composition is exact. Self, pets, and Free-PK creatures now query health while friendly NPCs and attackable non-creatures remain name-only. AP-46 retired. The world/radar policy matrix and exact DAT paperdoll body-map + upper-item-priority route passed live 2026-07-11.
|
||||
- **✓ SHIPPED — Wave 4.4d toolbar launchers + Use/Examine.** All seven authored launchers are discovered through their DAT panel-id attribute; mounted Inventory/Character panels toggle through the registry and the other five buttons are ghosted. Typed window-visibility events own Highlight/Normal state. Use and Examine act on selection or enter their one-shot retail target modes, with exact Appraise wire dispatch and retail cursors. Warning-free App build and 4,747-pass / 5-skip Release suite are green; the connected live gate passed 2026-07-11. AP-101 is narrowed to the remaining ammo-number display.
|
||||
- **Wave 4.4e implemented — exact missile ammo number (live gate pending).** Pure Core reproduces retail's thrown-weapon-vs-separate-ammo resolution over the ordered player equipment list and its zero-to-one count normalization. Relevant equipment/stack events update authored missile indicator `0x10000194` with the DAT font. Warning-free Release build and 4,754-pass / 5-skip suite are green; AP-101 is retired.
|
||||
- **✓ SHIPPED — Character window** (`LayoutDesc 0x2100002E`, `CharacterStatController`, 2026-06-26, same branch). **Visually user-confirmed 2026-06-26 — Attributes tab reads as retail.** Three tabs, header (name/heritage/PK), large-gold level number (dat font, `largeDatFont` 18px), "Total Experience (XP):" + "XP for next level:" captions, 9-row attribute list (icons + right-aligned values + Health/Stamina/Mana vitals), click-to-select (top/bottom selection bars + footer State-B "{Attr}: {value}" / "Experience To Raise: Infinity!" + affordability-gated raise triangles), centered footer. User noted "still needs some polish for later" — deferred to Issue #158.
|
||||
- **✓ SHIPPED — D.5.4 — Client object/item data model (foundation).** Shipped 2026-06-18 (`b506f53`..`a33e897`, 11 commits). Renamed `ItemRepository`→`ClientObjectTable` / `ItemInstance`→`ClientObject`; broadened the table to hold EVERY server object (retail `weenie_object_table` shape). `CreateObject` is now the canonical merge-upsert (`ClientObjectTable.Ingest`, retail `SetWeenieDesc` semantics) via a new Core.Net `ObjectTableWiring` (off GameWindow); `DeleteObject` evicts; `PlayerDescription` is a membership manifest (`RecordMembership`); live container-membership index (`GetContents`, retail `object_inventory_table`). `_liveEntityInfoByGuid` retired (selection/describe resolve from the one table). Root fix: the old enrich-existing-only `EnrichItem` dropped `CreateObject`s for items with no `PlayerDescription` stub — live-Coldeve 4/6 hotbar slots blank; items are now created, not dropped. **Crux resolved:** retail is TWO tables (`object_table` + `weenie_object_table`), NOT one — acdream's `WorldEntity` (3D system) + `ClientObjectTable` (data/UI) split was already architecturally faithful; the fix was the ingestion path, not a table unification. 2671 tests green.
|
||||
- **Roadmap correction (2026-07-10):** the completion order is now the architecture-first campaign in `docs/superpowers/plans/2026-07-10-retail-ui-fidelity-completion.md`. Retail `gmToolbarUI` is object-only: preserve `ShortCutData.index_`, `objectID_`, and `spellID_`, but do not invent spell glyphs on this bar. `PlayerModule::favorite_spells_[8]` feeds separate spell bars.
|
||||
|
|
|
|||
60
docs/research/2026-07-11-retail-toolbar-ammo-pseudocode.md
Normal file
60
docs/research/2026-07-11-retail-toolbar-ammo-pseudocode.md
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
# Retail toolbar ammunition pseudocode
|
||||
|
||||
## Named-retail oracle
|
||||
|
||||
- `gmToolbarUI::UpdateAmmoID @ 0x004BF210`
|
||||
- `gmToolbarUI::UpdateAmmoNumber @ 0x004BE9E0`
|
||||
- `gmToolbarUI::RecvNotice_ServerSaysMoveItem @ 0x004BF2B0`
|
||||
- `gmToolbarUI::RecvNotice_ItemAttributesChanged @ 0x004BF8F0`
|
||||
- `ACCWeenieObject::GetObjectAtLocation @ 0x0058CE00`
|
||||
|
||||
The Binary Ninja text corrupts the second `GetObjectAtLocation` constant into a
|
||||
string literal. The surrounding branch, retail `INVENTORY_LOC` header, ACE
|
||||
`EquipMask`, and holtburger equipment model agree that the two locations are
|
||||
`MISSILE_WEAPON_LOC = 0x00400000` and `MISSILE_AMMO_LOC = 0x00800000`.
|
||||
|
||||
## Literal pseudocode
|
||||
|
||||
```text
|
||||
UpdateAmmoID:
|
||||
oldAmmoID = ammoID
|
||||
ammoID = 0
|
||||
player = GetWeenieObject(playerID)
|
||||
if player exists:
|
||||
weaponID = player.GetObjectAtLocation(MISSILE_WEAPON_LOC, priority=0)
|
||||
if weaponID != 0:
|
||||
weapon = GetWeenieObject(weaponID)
|
||||
if weapon exists and weapon.maxStackSize > 1:
|
||||
ammoID = weaponID
|
||||
|
||||
if ammoID == 0:
|
||||
ammoID = player.GetObjectAtLocation(MISSILE_AMMO_LOC, priority=0)
|
||||
|
||||
if ammoID != oldAmmoID:
|
||||
UpdateAmmoNumber()
|
||||
|
||||
UpdateAmmoNumber:
|
||||
text = ""
|
||||
ammo = GetWeenieObject(ammoID)
|
||||
if ammo exists:
|
||||
count = ammo.stackSize
|
||||
if count == 0:
|
||||
count = 1
|
||||
text = decimal(count)
|
||||
SetText(toolbarElement 0x10000194, text)
|
||||
```
|
||||
|
||||
`GetObjectAtLocation` walks the player's ordered inventory-placement list and
|
||||
returns the first entry whose location mask intersects the requested mask.
|
||||
Stackable missile weapons therefore display their own count (thrown weapons);
|
||||
ordinary launchers display the separately equipped ammunition stack.
|
||||
|
||||
Move notices touching player equipment locations `0x00C00000` recompute the
|
||||
ammo object ID. Attribute/stack changes for the current ammo object refresh its
|
||||
number. Player-description arrival also performs an initial resolution.
|
||||
|
||||
## Port boundary
|
||||
|
||||
`ToolbarAmmoPolicy` is pure Core logic over the ordered player placement list.
|
||||
`ToolbarController` owns repository event subscriptions and writes the resulting
|
||||
decimal label into authored element `0x10000194`. Missing ammo is an empty label.
|
||||
|
|
@ -582,6 +582,16 @@ App build and full 4,747-pass / 5-skip Release suite are green. The user confirm
|
|||
launcher toggles, close-driven highlight clearing, unavailable-button ghosting, and
|
||||
selected/targeted Use and Examine behavior in the connected Release client.
|
||||
|
||||
**Wave 4.4e implementation (2026-07-11; live gate pending):** pure Core
|
||||
`ToolbarAmmoPolicy` ports `gmToolbarUI::UpdateAmmoID @ 0x004BF210` and
|
||||
`UpdateAmmoNumber @ 0x004BE9E0`. It walks the ordered player placements exactly
|
||||
like `GetObjectAtLocation`: a stackable missile weapon supplies its own count,
|
||||
otherwise the separate missile-ammo slot supplies the count; wire stack zero displays
|
||||
as one and no ammo displays blank. `ToolbarController` refreshes authored missile
|
||||
indicator `0x10000194` on relevant equipment and stack events and uses the DAT font.
|
||||
Focused pure-policy, retained-event, and real-toolbar-fixture conformance is green.
|
||||
The warning-free Release build and full 4,754-pass / 5-skip suite are green.
|
||||
|
||||
**Wave 4.1 implementation (2026-07-11):** bare `1..9` now use slots 0..8,
|
||||
Ctrl+`1..9` selects them, Alt+`5..9` uses slots 13..17, and `0` creates a
|
||||
shortcut to the selected owned/eligible object in the first empty slot. A focused
|
||||
|
|
@ -594,8 +604,7 @@ indicator buttons `0x10000192..0x10000195` now all dispatch one injected combat
|
|||
toggle command. The key binding and retained toolbar therefore converge on
|
||||
`GameWindow.ToggleLiveCombatMode`, matching `gmToolbarUI::ListenToElementMessage
|
||||
@ 0x004BEE90`; the visible indicator continues to update from authoritative
|
||||
`CombatState`. Raw shortcut preservation, drag/drop edge cases, and the toolbar
|
||||
ammo-number display remain in this wave.
|
||||
`CombatState`. Raw shortcut preservation and drag/drop edge cases remain in this wave.
|
||||
|
||||
**Oracles:** `gmToolbarUI @ 0x004BD0C0..0x004BF380`,
|
||||
`UIElement_UIItem::SetShortcutNum`, shortcut wire builders.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue