4.5 KiB
Retail item useability and AutoWear rejection
Date: 2026-07-23
Retail anchors
ItemUses::ItemUses @ 0x004FCCB0ItemUses::IsUseable @ 0x004FCCC0PublicWeenieDesc::Reset @ 0x005ACC70PublicWeenieDesc::UnPack @ 0x005AD470ItemHolder::DetermineUseResult @ 0x00588460ItemHolder::UseObject @ 0x00588A80CPlayerSystem::UsingItem @ 0x00562F70CPlayerSystem::AutoWearIsLegal @ 0x0055EF40CPlayerSystem::AutoWear @ 0x005601C0ACCWeenieObject::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:
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:
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
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. Event_UseEvent itself is the network send
(0xF7B1/0x36); the latter function handles local pickup/wield/container
presentation. Therefore neither an owned item nor a world object may be held
behind a client-side movement completion. ACE owns the authoritative approach
chain.
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
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.IsUseableis the one Core implementation of the low-bit rule. Toolbar enablement, item activation, and world interaction consume it.ItemInteractionControllerremains 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.SelectionInteractionControllersends accepted ordinary Use immediately for both owned and spatial objects. It does not install speculative local TurnToObject/MoveToObject movement; ACE's authoritative movement packets use the shared retailMoveToManager.AutoWieldControllerremains the shared inventory/paperdoll equip owner. It resolves AutoWear blockers fromClientObjectTable'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.