Inventory And World Items
The inventory subsystem keeps one server-authoritative item entity as it moves
between the world and an ability-defined unit inventory. The same edict is
retained across pickup and drop; normal transitions do not destroy and recreate
it. MAX_INVENTORY remains the six-slot storage/UI ceiling, while gameplay
capacity comes from the unit's inventory ability.
State Contract
Item lifecycle state lives on the game edict alongside the existing unit inventory pointers.
| State | carrier |
inventory_slot |
in_world |
Presentation |
|---|---|---|---|---|
| World | NULL |
-1 |
true |
Linked, visible, sent to clients |
| Carried | Unit edict | valid slot | false |
Unlinked, RF_HIDDEN, SVF_NOCLIENT; shown by inventory UI |
The item edict also owns its mutable charge count. SP_SpawnItem initializes
item.charges from ItemData uses (iuse); pickup and drop preserve it.
GetItemCharges and SetItemCharges read and update that same runtime state.
For a carried item, the carrier's matching inventory slot must point back to
the item. G_AddItemToSlot, G_PickupItem, G_DropItemAt, G_DropItem, and
G_RemoveItem own the world/inventory relationship.
Inventory Capability And Capacity
Inventory is ability-defined, not hero-defined. G_InventoryCapacity scans the
unit's normal ability list for an ability whose AbilityData code is AInv.
Its first data slot is Warcraft III field inv1 (Item Capacity). AB_Data
resolves the archive-version spelling (Data11 in ROC, DataA1 in TFT).
Capacity is clamped to the six-slot OpenRealm storage/UI ceiling.
Consequently a normal hero inventory resolves to six slots, while a custom inventory ability may expose fewer slots. Pickup, explicit slot insertion, client item use/drop commands, JASS slot operations, and HUD enumeration all respect the resolved capacity.
Contextual Pickup
A client right-click still produces the existing smart <entity> command.
The server recognizes a world item before harvest, attack, or move handling and
starts a pickup order for each selected inventory-capable unit.
The order keeps the item as its goal and checks it every simulation tick. It
moves while the center-to-center distance is greater than
ITEM_PICKUP_RANGE (150 world units), then attempts the authoritative
inventory transition. The order stops if the item was removed, hidden, picked
up by another unit, or otherwise left world state.
When every slot exposed by the inventory ability is occupied, the transition
does not modify either entity. The world item stays linked and visible, and
contextual pickup displays an Inventory is full. message to the owning player.
Inventory Presentation
The HUD has no item-specific branches. G_GetInventory walks only the selected
unit's exposed slots and G_BuildInventoryItem resolves presentation by the
carried item's rawcode through the already-loaded Warcraft III UI config tables:
item rawcode
-> ItemFunc.txt / ItemStrings.txt
-> Art / Tip / Ubertip
-> gameInventoryItem_t
-> LAYER_INVENTORY command button
Art is passed through the same theme indirection used by command-card art and
then registered through gi.ImageIndex when the server authors the inventory
frame. The parsed INI tables and image registry are already persistent engine
state, so no second item-UI cache is maintained.
The runtime charge count is copied into gameInventoryItem_t. WriteInventory
draws a bottom-right number overlay whenever charges > 0, including a
single-charge item. A zero-charge item therefore keeps its icon but has no
number overlay. Cooldown/disabled-state presentation and charge consumption are
separate active-item work.
For Human02 this means a carried Scroll of Protection is handled generically:
rawcode spro resolves its item UI data, appears in the first free slot, and
shows its initial charge count of 1. No HUD code checks for spro.
Selected-unit inventory panel state
Inventory visibility is capability-defined independently of hero presentation.
For one selected unit, LAYER_INVENTORY resolves G_InventoryCapacity and
authors one of three states:
- capacity
0: cover the underlying six-slot console area with the local player's race-skinConsoleInventoryCoverTexture; - capacity
1..5: leave the valid slots visible and cover each slot outside capacity withConsoleInventoryNoCapacity; - capacity
6: leave all six normal slots visible.
Both texture keys come from UI\war3skins.txt, using the local player's race
section with Default fallback. Classic ROC data used by OpenRealm does not
provide a usable inventory-cover FDF, so the cover is one of the WC3 native
frames constructed in C: a static FRAMEDEF owns the confirmed texture crop,
ALPHAKEY mode, 0.128 x 0.175 size, and bottom-right screen anchor. No
project-owned production FDF is shipped. The selected unit determines inventory
capability and contents; the local player's console skin determines cover/filler
artwork. Hero/non-hero stats remain an independent info-panel decision.
The cover samples only the useful lower portion of the packed console BLP
(V=0.380859375..1.0) rather than squeezing the complete image canvas into the
frame. This crop came from the bounded runtime asset diagnostic that established
the useful pixels begin at row 195 of the 512-pixel source image.
Stock non-hero inventory abilities are capability-gated by their Backpack
upgrade rather than becoming active merely because the alias appears in
UnitAbilities.slk. For example, the Footman carries Aihn in its authored
ability list, but its two inventory slots remain covered until Human Backpack
Rhpm is researched. The same rule applies to the stock Orc, Night Elf and
Undead unit-inventory variants. Plain AInv and custom AInv-derived inventory
abilities are not implicitly gated.
The current typed metadata layer does not yet normalize UpgradeData.slk
effects, so the stock inventory-ability-to-Backpack relationships are kept as a
small explicit table in g_items.c. Move that table into normalized upgrade
effect data when upgrade effects become authoritative runtime metadata.
Inventory Refresh Lifecycle
Player/client edicts occupy the reserved [0, max_clients) range and are not
ordinary inuse gameplay entities. Inventory refresh therefore iterates those
reserved client slots and gates on the explicit GAMECLIENT.connected state.
G_ClientBegin marks the slot connected after the handshake, while map-player
initialization clears the state before the next map begins.
A previous refresh path incorrectly required the reserved player edict itself to
be inuse. Pickup still completed, but the refresh was skipped and the server
never resent LAYER_INVENTORY.
Item-state changes refresh only LAYER_INVENTORY through
G_RefreshInventoryLayer. They do not rebuild the portrait or info panel. This
keeps item transitions independent of unrelated portrait/FDF presentation and
avoids requiring a full selected-unit HUD rebuild merely because an item moved
or its charge count changed.
The bounded diagnostic for this boundary is:
A successful carried-item refresh should produce a new layer=6 layout write;
an occupied item contributes at least one textured command-button frame.
Drop And Script Paths
G_DropItemAt performs the inverse transition and places the same item edict
at a requested world position. G_DropItem uses the carrier's current position.
The existing JASS natives route through this lifecycle:
UnitAddItemUnitAddItemByIdUnitAddItemToSlotByIdUnitRemoveItemUnitRemoveItemFromSlotRemoveItemSetItemPositionfor items already in world stateGetItemChargesSetItemCharges
The client command dropitem <slot> exposes a direct zero-based drop path for
the main selected unit. Inventory drag/drop interaction is a later UI phase.
Successful transitions and carried-item charge changes refresh the inventory layer for clients currently selecting the carrier. Hidden entities are also excluded from renderer hit and rectangle tests while snapshot removal is in flight.
Phase Boundary
This slice includes generic item icon/tooltips, ability-defined capacity, and runtime/displayed charges. It deliberately does not consume charges, destroy a perishable item at zero, implement automatic use, active target modes, item-use cooldowns/disabled icons, slot swapping, giving, or death-drop rules. Existing passive-effect hooks remain attached to inventory entry and exit.
The implementation is derived from observable behavior and Warcraft III data formats described by the clean-room specification. It does not depend on another engine's item implementation.
Validation
The wc3_items.* in-engine tests cover world-state initialization, data-driven
capacity (including reduced, zero, and above-storage-limit cases),
first-empty-slot insertion,
full-inventory failure, pickup range and revalidation, drop identity, renderer
visibility flags, carried-item removal, connection-state refresh gating, charge
initialization/preservation, carried-charge refresh/no-op behavior, JASS charge
access, and generic spro Art/Tip/Ubertip/charge presentation.
Minimal AbilityData.slk, UnitAbilities.slk, ItemData.slk, ItemFunc.txt,
ItemStrings.txt, and war3skins.txt fixtures keep these tests data-driven in
both ROC and TFT test runs. Inventory-panel tests additionally cover the
no-inventory cover, reduced-capacity fillers, full-capacity absence of fillers,
race-skin selection, and the native cover frame's crop/geometry. Synthetic
ability-only unit IDs such as H001 have no UnitBalance life value, so the
shared test-unit allocator initializes a minimum positive life value without
weakening the runtime rule that zero-life units are corpses and cannot be selected.