acdream/docs/research/2026-07-11-retail-toolbar-ammo-pseudocode.md
Erik 564d39dfea 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>
2026-07-11 12:29:51 +02:00

2.2 KiB

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

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.