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)
fis 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:
- Attenuation no longer applies
current_carry_massjumps- 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):
- Place a building with facility storage (tag
storageorfacility: storage). - Author interior; place N Storage Manager NPCs (
role: storage_manager). - 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.
- Place a building with facility bank (tag
bank). - Place N Bank Teller NPCs inside (
role: bank_teller). - Player enters building → walks to a teller →
finteract → 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):
- 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.
- 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:
- Validate mass & volume after modifiers
- Validate packable + nesting depth + slot compatibility
- Validate soul-bound / binding rules
- Emit audit event for high-value transfers and bank ops
- 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);tstows selected carry item; Enter on storage takes out; Tab switches panes;llocks/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