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
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.
|
||||
Loading…
Add table
Add a link
Reference in a new issue