acdream/docs/research/2026-07-23-retail-item-use-and-autowear-pseudocode.md

124 lines
4.5 KiB
Markdown

# Retail item useability and AutoWear rejection
Date: 2026-07-23
## Retail anchors
- `ItemUses::ItemUses @ 0x004FCCB0`
- `ItemUses::IsUseable @ 0x004FCCC0`
- `PublicWeenieDesc::Reset @ 0x005ACC70`
- `PublicWeenieDesc::UnPack @ 0x005AD470`
- `ItemHolder::DetermineUseResult @ 0x00588460`
- `ItemHolder::UseObject @ 0x00588A80`
- `CPlayerSystem::UsingItem @ 0x00562F70`
- `CPlayerSystem::AutoWearIsLegal @ 0x0055EF40`
- `CPlayerSystem::AutoWear @ 0x005601C0`
- `ACCWeenieObject::GetObjectAtLocation @ 0x0058CE00`
The named pseudo-C is the behavioral oracle. The matching v11.4186 binary was
also inspected because an older project note interpreted
`ItemUses::IsUseable` as a nonzero test. The actual instructions are:
```text
004fccc0 mov eax,[ecx]
004fccc2 not eax
004fccc4 and eax,1
004fccc7 ret
```
Therefore the method tests only `USEABLE_NO` (bit zero). A zero-valued
`ITEM_USEABLE` is usable; `USEABLE_NEVER_WALK` is a movement modifier and does
not make the object unusable. The literal AutoWear rejection at `0x007CC868`
is:
```text
You must remove your %s to wear that
```
The staged ACE, holtburger, and ACViewer reference directories are currently
empty in this checkout. Their established roles were still checked: ACE owns
the authoritative use/wield outcome, while the two client references do not
replace the retail UI-side legality and notice oracle. No server behavior is
being inferred or moved client-side here.
## Pseudocode
```text
ItemUses.IsUseable():
return (useableBitfield & USEABLE_NO) == 0
```
`PublicWeenieDesc::Reset` zeros the useability field. `UnPack` replaces it only
when the optional useability word is present. Consequently an absent wire word
and an explicitly packed zero both have retail `USEABLE_UNDEF` value zero and
both pass `IsUseable`.
For a direct-use item, `ItemHolder::UseObject` then sends
`CM_Inventory::Event_UseEvent`, increments the UI busy count, and calls
`CPlayerSystem::UsingItem`. That latter presentation function handles local
pickup/wield/container state; it does not require a carried source to have a
world position. Therefore an owned inventory object must cross the wire
directly. Only world objects participate in acdream's registered AD-27 local
approach compatibility seam.
```text
AutoWearIsLegal(itemId, out alreadyWorn, silent):
alreadyWorn = false
if player is not ready for an inventory request:
return false
item = GetWeenieObject(itemId)
if item is null:
return false
if (item.validLocations & 0x08007FFF) == 0:
return false
if (item.priority & playerSystem.clothingPriorityMask) == 0:
return true
player = GetWeenieObject(playerId)
if player contains item at a worn location:
alreadyWorn = true
if not silent:
if alreadyWorn:
display "The <item appropriate name> is already being worn"
else:
occupied = playerSystem.inventoryMask & item.validLocations
blocker = player.GetObjectAtLocation(occupied, item.priority)
if blocker exists:
display "You must remove your <blocker appropriate name> to wear that"
return false
```
```text
AutoWear(itemId, out alreadyWorn, silent):
if not AutoWearIsLegal(itemId, alreadyWorn, silent):
return false
item = GetWeenieObject(itemId)
if item exists:
item.UIAttemptWield(item.validLocations)
return true
```
## Port boundary
- `ItemUseability.IsUseable` is the one Core implementation of the low-bit
rule. Toolbar enablement, item activation, and world interaction consume it.
- `ItemInteractionController` remains the shared double-click and toolbar-hand
path. A zero-valued carried gem such as Blackmoor's Favor therefore emits the
ordinary Use request directly from its inventory identity; it is never sent
through the world-approach query. ACE remains authoritative for its spell
and consumption outcome.
- `SelectionInteractionController` keeps the AD-27 turn/approach seam only for
non-owned spatial objects. Owned self, equipped, and nested-container sources
have no live world pose and send immediately.
- `AutoWieldController` remains the shared inventory/paperdoll equip owner. It
resolves AutoWear blockers from `ClientObjectTable`'s retail-ordered
equipment projection and emits the retail literal through system chat.
- Weapon replacement is unchanged. Retail automatically moves primary weapon,
incompatible shield, and incompatible ammunition blockers, while AutoWear
rejects overlapping clothing/armor and tells the player what to remove.