Skip to content

feat: Inventory Management & Equipment System (spec #27) #28

@Fortinbra

Description

@Fortinbra

Feature Spec: Inventory Management & Equipment System

Field Value
Feature Inventory Management & Equipment System
Issue #TBD
Status Draft
Author The Doctor
Date 2026-04-10

Overview

What it does

Introduces a character inventory system backed by the Item Catalog (spec #6). Characters can carry items, equip gear into defined slots, and transfer items under DM control. The /inventory Discord command shows a player’s current loadout.

Why it's needed

Inventory management is essential for loot progression, combat equipment, and player agency. This spec connects items to characters and enables the AI Action System to award items.

Out of scope

  • Crafting or item upgrades
  • Encumbrance penalties beyond weight tracking
  • Merchant economy or trading (future spec)

Architecture Notes (The Doctor)

Projects / layers touched

  • DungeonMaster.Core — inventory entities + services
  • DungeonMaster.Infrastructure — EF Core mappings + migrations
  • DungeonMaster.Api — inventory endpoints
  • DungeonMaster.Bot/inventory command
  • DungeonMaster.Shared — inventory DTOs

New interfaces in DungeonMaster.Core

  • IInventoryService — add/remove/equip/transfer items

Integration points with existing systems


Domain Model

Entities

CharacterInventory

  • PlayerCharacterId (Guid FK, unique)
  • Items (ICollection)

InventoryItem

  • CharacterInventoryId (Guid FK)
  • ItemId (Guid FK)
  • Quantity (int)
  • EquippedSlot (EquipmentSlot? nullable)

Enums

EquipmentSlot

  • MainHand
  • OffHand
  • Armor
  • AccessoryOne
  • AccessoryTwo

Records

public sealed record InventorySnapshot(
    Guid PlayerCharacterId,
    IReadOnlyList<InventoryItemDto> Items,
    int TotalWeight);

Service Interfaces (CQRS)

public interface IInventoryService
{
    Task<InventorySnapshot> GetInventoryAsync(Guid playerCharacterId, CancellationToken ct = default);
    Task AddItemAsync(Guid playerCharacterId, Guid itemId, int quantity, CancellationToken ct = default);
    Task RemoveItemAsync(Guid playerCharacterId, Guid itemId, int quantity, CancellationToken ct = default);
    Task EquipItemAsync(Guid playerCharacterId, Guid itemId, EquipmentSlot slot, CancellationToken ct = default);
    Task UnequipItemAsync(Guid playerCharacterId, Guid itemId, CancellationToken ct = default);
    Task TransferItemAsync(Guid fromCharacterId, Guid toCharacterId, Guid itemId, int quantity, CancellationToken ct = default);
}

API Contract (Rory)

Endpoints

  • GET /api/characters/{id}/inventory
  • POST /api/characters/{id}/inventory/items — add item (DM-only)
  • DELETE /api/characters/{id}/inventory/items/{itemId} — remove item (DM-only)
  • POST /api/characters/{id}/inventory/equip — equip item (player/DM)
  • POST /api/characters/{id}/inventory/unequip — unequip item
  • POST /api/characters/{id}/inventory/transfer — DM transfer between characters

Discord Commands (Rory — Bot Layer)

  • /inventory — shows character inventory summary

Test Scenarios (Danny) ⚠️ COMPLETE BEFORE IMPLEMENTATION

Happy path

  1. Given a character inventory exists
    When the DM adds an item
    Then the item appears in inventory with correct quantity

  2. Given a character equips a weapon
    When EquipItemAsync runs
    Then the item is marked with slot MainHand

Edge cases

  1. Given an item is already equipped
    When it is equipped again
    Then the request is rejected with a validation error

  2. Given a transfer exceeds quantity
    When TransferItemAsync runs
    Then the request fails with 400 Bad Request

Error / failure cases

  1. Given an invalid item ID
    When AddItemAsync runs
    Then the API returns 404 Not Found

  2. Given a non-DM user attempts to add items
    When POST /inventory/items is called
    Then the API returns 403 Forbidden


Acceptance Criteria

Functional

  • Inventory is stored per character with items and quantities
  • Equipment slots enforce one item per slot
  • Transfers move items between characters
  • /inventory command shows current loadout
  • AwardItemAction integrates with IInventoryService

Non-functional

  • Inventory queries respond within 200 ms
  • No hardcoded item names; items resolved via Item Catalog
  • All Discord IDs stored as bigint

Dependencies


Agent Work Breakdown

Agent Task Depends On
The Doctor Approve spec
Danny Write failing tests from Test Scenarios Spec approved
Rory Implement inventory entities + services Danny's tests
Rory Add API endpoints + Bot /inventory Core entities
Danny Confirm all tests pass All implementation

Definition of Done

  • Test Scenarios section completed and approved by The Doctor before implementation
  • All failing tests written by Danny before implementation (TDD)
  • All tests written and passing (xUnit + bot command tests)
  • Code reviewed and approved by The Doctor
  • EF Core migration created for inventory tables
  • GitHub issue closed and linked to merged PR

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestspecSpecification work

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions