Home Inventory & Storage

Inventory, Carriers & Storage


1. Summary

Inventory is a dual-constraint system tracking unique item instances (18) — not abstract “gear.”

  • Mass (weight) — total carried mass vs effective Strength (lift capacity)
  • Volume — each container has capacity; items consume volume slots or units

Items and containers both have intrinsic mass and volume. Nested storage sums intelligently: a bag’s contribution to what you lift is its empty mass + contents, subject to modifiers (spells, magic bags, buffs).

Players manage what they wear, what they carry, what they bank, and what they risk on death (on-person graph → corpse).


2. Core concepts

2.1 Item properties (every instance)

Property Meaning
instance_id Global UUID — every mint is unique (18)
template_id Schema kind (longsword, oak_log, …)
quantity 1 for equipment/meals; splittable for bulk materials
stat_roll Per-instance damage, armor, etc. (18)
base_mass Kilograms (or game units) — intrinsic weight (may vary by roll)
base_volume Space occupied when stored
stackable Optional stack with combined mass/volume rules
packable Can be placed inside another container (§4.3)
can_soul_bind Eligible for soul-bind ritual (07 §4.3)
enchantable Default true for all instances; opt-out per template (17)
allowed_enchant_tags Tags for matching enchant recipes (17)
soul_bound Instance flag — stays on character at death
enchantments Instance: Vec<EnchantmentInstance> — procs + passive mods (17)
flags no_magic_mass, dimensional, etc.

2.2 Container properties (bags, robes, chests)

Property Meaning
base_mass Empty container weight
base_volume Size when carried elsewhere
capacity_volume Max sum of contained items’ volumes
capacity_rules slot grid vs pure volume vs hybrid
carrier_kind worn, held, ground, static storage
packable Can this container be put inside another bag? (§4.3)

Dimensional / magical bags typically set packable: false — they don't nest inside mundane backpacks. They still attach to a worn belt's loops via belt_loop: true (§4.1), since loop attachment is a separate mechanism from generic bag-in-bag packing and ignores packable.

2.3 Effective mass of a nested container

Default (physical realism):

effective_mass(container) =
    modifier_apply(container.base_mass)
  + sum(child.effective_mass for child in container.contents)

Exception — dimensional / magic storage (§5): contained children may use attenuated mass while inside.

2.4 Effective volume

Contained items cannot exceed capacity_volume (after modifiers). Equipped containers still occupy volume on the parent body slots (e.g. large backpack uses back slot + its own volume footprint).


3. Strength & carry capacity

3.1 Strength attribute

Strength determines maximum lift mass the character can carry on-person:

carry_mass_max = f(base_strength, skill_training, modifiers)
current_carry_mass = sum(root_carry_graph.effective_mass)
  • f is monotonic — higher Strength → higher capacity
  • Training Strength skill (and general physical training) raises base capacity over time
  • Modifiers (§6) temporarily shift effective Strength or bypass mass for specific items

3.2 Over-encumbered states

State Condition Effect
Light < 50% max Normal movement
Heavy 50–100% Reduced speed, stamina drain
Over > 100% Cannot move / cannot pick up; must drop items
Blocked volume full Cannot add items until reorganize

Server validates every pickup, equip, and container transfer against both mass and volume.


4. Personal carry layout

4.1 Slots (on-person) — shipped (2026-07-09)

Character
├── worn: BTreeMap<BodySlot, ItemInstance>   — one item per slot, at most
│   ├── Head   (armor — cosmetic placeholder today, no stats yet)
│   ├── Body   (armor — cosmetic placeholder today, no stats yet)
│   ├── Arms   (armor — cosmetic placeholder today, no stats yet)
│   ├── Legs   (armor — cosmetic placeholder today, no stats yet)
│   ├── Feet   (armor — cosmetic placeholder today, no stats yet)
│   ├── Back   (backpack — capacity_volume container, unchanged since 3b)
│   └── Waist  (belt — not a volume container; gates attached pouches by count)
├── hands (mainhand, offhand, two-hand rules)
└── root_inventory (abstract “person” node for graph)

BodySlot replaces the earlier hardcoded back_container / belt_container pair with a generic 7-variant enum — any number of slots can be occupied simultaneously (this is what makes wearing a backpack and a belt and a hat all at once possible, rather than the old two-field special case).

Belt loops, not a body slot of their own. A belt (simple_belt, utility_belt) equips into Waist like any other wearable, but pouches (leather_pouch, dimensional_pouch) no longer have an equip_slot — they set belt_loop: true instead and attach to a worn belt's loops via ordinary MoveItem (to: Worn{slot: Waist}), the same mechanism used to pack a pouch into a backpack. A belt declares belt_loop_capacity (e.g. 2 for simple_belt, 4 for utility_belt) — a count limit on direct belt_loop children, independent of capacity_volume/mass. This is what makes even a tiny dimensional pouch scarce: you can carry at most belt_loop_capacity of them regardless of how little they weigh. Unequipping a belt carries its clipped pouches with it for free, since they're already nested in its contents.

Wearable containers (backpack, belt) attach to slots and expose child container inventories; armor slots are equip-only today (no nested contents, no stat effects yet — a future pass wires Head/Body/Arms/Legs/Feet items into derived_from alongside 11-entities-attributes-and-movement.html's attribute/skill modifiers).

4.2 Nested containers & packable (decided)

Bags inside bags is allowed — with strict packable rules.

Check Rule
Child packable Must be true on item template
Parent accepts Container allows child category (volume + slot rules)
Depth limit max_container_nest_depth from ruleset config (default 4) — not hardcoded in code
Magic bags inside bags Denied — dimensional bags packable: false
Soul-bound container Cannot be placed inside another container if policy says no
Valid:   backpack → mundane pouch → ore (packable)
Invalid: backpack → dimensional_haversack (packable: false)
Invalid: backpack → another_large_backpack (if template packable: false)

Server validates on every move_item into container ancestry.

4.3 Nest depth configuration

# area ruleset or global game_config
inventory:
  max_container_nest_depth: 4   # default; tunable per ruleset / Area manifest

Server reads at Area boot; validate depth + 1 <= max on each pack operation.

4.4 Death interaction

Only the on-person carry graph moves to corpse (see 07-death-and-respawn.html §4). Bank balance and town item storage are separate. Soul-bound items never enter corpse snapshot.

4.5 Ground drops (carrier_kind: ground)

NPC death loot and unclaimed items use ground carriers — not auto-added to killer inventory.

Property Rule
Spawn Death roll mints GroundDrop at world position (scatter disk)
Pickup Pickup intent within 1.5 m; mass/volume validated
Ownership Killer-only until ffa_delay_ticks, then anyone
Despawn loot_ttl_ticks unless permanent: true on table entry
Corpse vs drop Carcass = butcher harvest node; drops = instant rolls on ground

Full lifecycle (respawn, carcass TTL, FFA): 25-npc-lifecycle-and-ground-loot.html.


5. Magic & dimensional storage

5.1 Design intent

Magic enables more volume and effective mass reduction while items are inside special containers — but removing an item restores normal mass, forcing moment-to-moment load management.

5.2 Dimensional bag (example)

Property Value
capacity_volume Very large
mass_attenuation e.g. 0.1× on contents while inside
container.base_mass Small (the bag itself is light)

When player withdraws a heavy ingot:

  1. Attenuation no longer applies
  2. current_carry_mass jumps
  3. Player may become over-encumbered until they repack, use Strength buff, or cast mass reduction on item

5.3 Spell: reduce item mass (temporary)

Target single item or container:

item.effective_mass *= modifier until expiry
  • Useful for hauling loot to town
  • Stacks with policy caps (anti-exploit)
  • Dispel or death may clear buffs on items on corpse unchanged

5.4 Anti-exploit

  • Attenuation only inside tagged dimensional containers (item type flag)
  • Mass restoration on withdraw is instant — server recalculates before allowing move
  • Audit unusual mass (central) for economy anomalies

6. Unified modifier system

All skills, attributes, and items use a common modifier model:

struct Modifier {
    id: ModifierId,
    source: Source,       // spell, potion, equipment, aura, item enchant
    target: Target,         // strength, carry_max, item_mass, container_volume, skill_xp_rate
    op: Add | Mul | Override,
    value: f32,
    expires_at: Option<Tick>,
}

Examples:

Source Target Effect
Strength potion attribute.strength +10 Add, 10 min
Berserker aura carry_max ×1.2 Mul
Featherweight spell item.mass (one item) ×0.5 Mul, 5 min
Dimensional bag contents.mass ×0.1 Mul while inside
Training buff skill.strength.xp_rate ×1.5 Mul
Flame Edge (weapon) on_hit → fire DoT on target proc (17, 12)
Vitality Charm (necklace) derived.hp_regen ×1.08 Mul while equipped

Recompute derived stats (carry max, item mass, container capacity) each tick or on change event — cache invalidation on modifier add/remove.


7. Off-person item storage

7.1 Storage tiers (locked 2026-07-29)

Tier How it works Security
Town storage building Building tagged/kind storage + Storage Manager NPC(s) inside Secure — not stealable
Player residence / plot chests Placed containers on owned land (20, 40) Insecure if unlocked or key shared — other players can open
Ground / corpse World containers Area-local timers; lootable

Town storage is not a free floating abstract vault UI — players enter the building, find a Storage Manager, and interact (f) like Ada’s shop.

7.2 Town storage building (content-authored)

Designer flow (content-admin / map):

  1. Place a building with facility storage (tag storage or facility: storage).
  2. Author interior; place N Storage Manager NPCs (role: storage_manager).
  3. Configure on the building (or facility block):
Field Meaning
max_volume_per_player Free included volume for that building
extra_volume_copper_per_day Rent for additional volume beyond the free cap

Per-player vault contents live on the central ledger (resolved I4) keyed by (character_id, storage_building_id).

7.3 Operations at a Storage Manager

  • Store / withdraw items — same UX as opening a chest (move items ↔ person), but vault is secure and capacity-capped.
  • Must be inside the storage building and in interact range of a manager.
  • Ship to another storage building — manager can queue a transport of selected goods to another storage facility: fee + travel time from distance. Items leave source vault and arrive after the timer (no mid-route steal for v1).

7.4 Risk loop

Location On death / theft
Bank balance Safe
Town storage vault Safe
Unlocked / keyed chest on plot Lootable by anyone with access
Coins & gear on person Corpse risk

8. Currency & banks (locked 2026-07-29)

8.1 Physical currency

Coins have mass and volume like any item. Denominations (configurable exchange rates):

Coin Typical use
Copper Wages, small goods, worker pay per tick
Silver Craft materials, broker trades
Gold Large purchases, property
Platinum Rare, high-end, cross-region arbitrage

Exchange: fixed ratio in ruleset (e.g. 100 copper = 1 silver, 100 silver = 1 gold, 100 gold = 1 platinum). Bank ledger is copper-normalized for v1 (physical coins still mint as denomination stacks on withdraw).

Property Behavior
base_mass Per coin or per stack unit
base_volume Purse / sack slot consumption
Stacks Combine mass/volume when stacked

Carrying large sums requires Strength and container space — encourages banking before long trips.

8.2 Bank buildings & tellers

Banks are buildings, not abstract panels.

  1. Place a building with facility bank (tag bank).
  2. Place N Bank Teller NPCs inside (role: bank_teller).
  3. Player enters building → walks to a teller → f interact → deposit / withdraw / transfer UI.
Action Where Notes
Deposit At a teller Physical coins on person → central bank balance
Withdraw At a teller Balance → physical coins (mass appears on person)
Balance inquiry Anywhere (CLI/HUD optional) Magical ledger read — no coins move
Account transfer Initiate at a teller (v1) See §8.3
Expense debit Automatic Wages, taxes, shipping, shops, hire/train, upkeep — bank ledger first, then purse (wages also lodging/chests). Paycheck-style; deposit never pulls from ledger.

No remote physical withdraw/deposit — keeps coin weight meaningful.

8.3 Magical clearinghouse (bank ↔ bank)

Hybrid (locked):

  1. Ledger transfer — copper moves on the central account after a short clear time (minutes-scale, settings), flat or light fee. Lore: world-magic clearinghouse shared by all bank branches. No wagon simulation.
  2. Optional secure courier (later) — larger sums / cross-region: longer delay + higher fee (still a timer, not a physical escort in v1).

Teller required to start a transfer keeps branches useful; magic clears the balance so everyday pay is not painful.

Asymmetry with storage: currency clears magically; item shipments between storage managers use distance-based cost and time (unique instances).

8.4 Death & currency

Form On death
Bank balance Safe
Coins on person On corpse with other gear
Coins in unlocked chests Stealable / lootable
Soul-bound coin purse (if such item exists) Stays with character if bound

9. Map items & cartography

Map items have base_mass and base_volume like any item. Atlases are heavier. Cartography ledger is not weight — it is character metadata (see 06).


10. Server authority

All inventory mutations:

  1. Validate mass & volume after modifiers
  2. Validate packable + nesting depth + slot compatibility
  3. Validate soul-bound / binding rules
  4. Emit audit event for high-value transfers and bank ops
  5. Replicate container diffs to client on change (reliable channel)

Death: snapshot on-person graph → corpse entity; strip from character in one transaction.

Banks / town storage: only mutate via teller/manager interact while inside the correct building; vault/bank balance never sits in stealable placed-container state.


11. TUI / agent views

  • Carry panel (b): mass bar (current / max), nested tree of worn bags + root inventory
  • Storage pane: open worn bag or nearby chest (o); t stows selected carry item; Enter on storage takes out; Tab switches panes; l locks/unlocks nearby chest (needs key)
  • Bank panel (teller): balance, deposit, withdraw, transfer
  • Town storage panel (manager): vault grid + ship-to-other-storage
  • CLI: flatland inv tree, flatland inv mass, flatland bank balance, flatland bank transfer

12. Phasing

Phase Scope
3a ✅ Mass + Strength carry cap + encumbrance (Heavy/Over)
3b ✅ Wearable back/belt, volume, packable nesting (depth 4), placeable chests, lock+key
3c Tier 4.13 — bank buildings + tellers; copper-normalized balance; deposit/withdraw; balance inquiry; ledger transfer
3c.2 Town storage buildings + managers; per-player volume + rent; ship between storages
3d Modifier system + potions/spells on stats
3e ✅ Lite: dimensional pouch mass_attenuation; full item mass spells later
3f Soul-bind ritual + eligible items
4+ Player housing chests keyed to property plots (40)

13. Open questions

Resolved (2026-07-06):

# Decision
I1 Kilograms (kg)
I2 Hybrid — volume capacity + slot grid
I3 Nest depth 4 default, tunable per ruleset
I4 Central ledger for residence/static storage contents

Resolved (2026-07-29):

# Decision
I5 Banks/storage are buildings + NPCs (teller / storage manager), Ada-style interact
I6 Bank transfers = magical clearinghouse (short delay + fee); not pure distance wagons
I7 Storage inter-building moves = distance cost/time
I8 Placed chests remain stealable if unlocked/keyed; bank & town storage are secure
I9 Bank ledger copper-normalized for v1

14. Design feedback

Dual mass + volume is the right call: Strength gates heaviness; containers gate hoarding on a trip. Nested effective mass with magic attenuation only inside bags creates the “pull one item out and suddenly you’re overloaded” moment you described — good emergent gameplay.

Modifiers on everything should be one system early (even if few effects at first) to avoid rewriting when spells arrive.

Packable flag prevents nesting absurdities (bag of bags of bags of dimensional storage) while still allowing organize-within-organize for mundane goods.

Weighted coins + magical bank ledger + building tellers splits the difference: hauling loot matters, everyday commerce clears through world magic, and branches stay places you walk into.

Town storage shipping keeps logistics meaningful for unique item instances without making copper transfers miserable.


Death and corpse rules: 07-death-and-respawn.html
Property / farm unlocks that key housing storage: 40-property-plots-and-farming.html