# InventoryApi ## Purpose Owns item-instance state. This service should answer: - what item records currently belong to a character, location, or other container owner - where each item currently is - whether an item move, split, merge, equip, or consume action is valid - which specific item instance is being traded, auctioned, or transferred This service should not own: - auth/token issuance - character identity creation - location generation - auction bidding logic ## Ownership model - every inventory record has an `ownerType` and `ownerId` - `USER` can read and mutate items owned by their own characters - `USER` can read and mutate location/container items only when gameplay rules allow it - `SUPER` can read and mutate any inventory item - access should be resolved through the current owner, not from the item id alone ## Initial design Use one MongoDB document per inventory item record. Practical interpretation: - non-stackable items: one document per item instance - stackable items: one document per stack Reasons: - every item or stack gets a stable UUID, which is useful for auctions, trade, mail, and auditing - ownership transfer is explicit and cheap: update `ownerType`, `ownerId`, and slot fields - future item metadata like durability, rarity rolls, or provenance can live on the item document - auction listings can point to specific item ids instead of vague stack descriptions Tradeoffs: - inventory reads are queries over many documents instead of one aggregate read - stack merging and slot enforcement need careful indexes and mutation logic - transfers should use transactions when they touch multiple item documents ## Suggested endpoints - `GET /api/inventory/by-owner/{ownerType}/{ownerId}` Return all item records currently owned by that container owner. - `POST /api/inventory/by-owner/{ownerType}/{ownerId}/grant` Create a new item record or add quantity into an existing compatible stack. - `POST /api/inventory/by-owner/{ownerType}/{ownerId}/move` Move an item or stack within the same owner inventory. - `POST /api/inventory/transfer` Move quantity from one owner inventory to another. - `POST /api/inventory/items/{itemId}/consume` Consume quantity from a specific item record. - `POST /api/inventory/items/{itemId}/equip` Equip a specific item into a character equipment slot. - `POST /api/inventory/items/{itemId}/unequip` Return an equipped item to a character inventory slot. Notes: - `equip` and `unequip` only make sense for character-owned items - `transfer` is the core world interaction primitive for looting, dropping, trading, chest interaction, and auction handoff - a future AuctionApi can reserve or re-own specific item ids without redesigning InventoryApi ## Item identity Every inventory record should have a stable UUID string such as: - `a8d4218b-5e20-4e47-8b5f-0f0f0b9d7e10` Each record also carries an `itemKey` such as: - `wood` - `stone` - `pistol` - `small_health_potion` Recommended distinction: - `id`: unique item-record identifier used for ownership changes, auctions, and references - `itemKey`: item definition identifier used to decide stackability and gameplay behavior ## Recommended stored shape Each item document should include: - `id` - `itemKey` - `quantity` - `ownerType` - `ownerId` - `slot` - `equippedSlot` - `ownerUserId` when applicable - `createdUtc` - `updatedUtc` Optional future fields: - `durability` - `rarity` - `instanceData` - `listingId` - `reservedUntilUtc` ## MVP rules - an item record must belong to exactly one owner at a time - stackable items may share `itemKey` but should still be represented by one stack record per occupied slot - non-stackable items must always have `quantity = 1` - equipped items should set `equippedSlot` and clear `slot` - unequipped bag items should set `slot` and clear `equippedSlot` - slot occupancy must be unique per `(ownerType, ownerId, slot)` - all mutating endpoints should be idempotent where practical ## Client shape The Godot client should fetch all items for the currently relevant owner and group them into a bag view locally. Good pattern: - login/select character - `GET /api/inventory/by-owner/character/{characterId}` - when opening a stash or world container: `GET /api/inventory/by-owner/location/{locationId}` - cache the returned item records locally - call transfer/equip/consume endpoints using specific `itemId` values - replace local state with the server response after each mutation