Inventories & holdings

Container

container-1

Tests included

This contract defines the behaviour of containers: a backpack, a chest, or a set of slots.

It decides when a container is full, what happens to items that do not fit, and where items go in and come out.

Use in your game

Opens the authoring tool with this contract added and its questions unanswered. Or download the ZIP and add it later.

What is in the ZIP

The ZIP holds the contract and its acceptance tests. In the authoring tool, choose Add contract and pick this ZIP. If you keep your design as files, put them in contracts/ and fill in the answers there.

Questions

Up to 6 questions. Some appear only after earlier answers.

  1. What makes this container full?
  2. A delivery reaches the container, but not all the items fit. What goes in?
  3. What happens to the items that do not fit?
  4. Several slots could hold a new item. Which slot gets it?
  5. The game needs an item for one of its own rules. Which matching slot does it take from?
  6. Which kinds of item can this container hold?

Try the answers

Pick answers to see which rules and tests apply. Nothing is saved here: the ZIP and the authoring tool start unanswered.

What makes this container full?

Choices for What makes this container full?
A fixed number of slots makes it full. A shop's twenty stock slots refuse a new kind of item when all twenty hold stacks. A matching stack with room can still accept it.
A size-budget container has no slots. A card deck with a thirty-point build budget may spend those points across cards of different costs.
A slot limit and a size budget both apply. A bag with three free slots can still refuse an anvil when its weight budget is full.
It never fills. A new item always has room, so nothing is refused because the container ran out of space.
Why this is asked

Capacity changes what the container can hold and how it fills. This contract supports one size measure. Containers where items occupy a width-and-height footprint are outside version 1.

A delivery reaches the container, but not all the items fit. What goes in?

Asked when
Capacity limit is Slot count or Size budget or Slots and size.
Choices for A delivery reaches the container, but not all the items fit. What goes in?
The whole delivery is refused. A ten-arrow pickup adds no arrows when the container has room for only five. Each offered type and amount is one delivery. A craft with several outputs makes several deliveries, so some outputs may enter before another is refused. The refused items stay with whatever offered them, and what that action does next is the game's own rule.
Everything that fits goes in. A ten-arrow pickup adds five when only five fit; the other five remain outside.
Why this is asked

Two containers with the same free space can act differently. One accepts what fits; the other refuses the whole delivery.

What happens to the items that do not fit?

Asked when
Partial acceptance is Fill what fits.
Choices for What happens to the items that do not fit?
The leftover goes back to its source. Arrows that do not fit return to the chest or pickup that offered them.
The leftover stays where the delivery arrived. Arrows that do not fit become a loose pickup beside the bag.
The leftover moves to another store named by your game. Arrows that do not fit may go to a stash.
The leftover is destroyed. Arrows that do not fit disappear instead of returning, dropping, or moving elsewhere.
Why this is asked

Items that stay outside still need a clear home. Otherwise one build may return them while another drops or destroys them.

Several slots could hold a new item. Which slot gets it?

Asked when
Capacity limit is Slot count or Slots and size or No limit.
Choices for Several slots could hold a new item. Which slot gets it?
Matching stacks fill before empty slots open. A potion tops up the first potion stack with room, then uses the first empty slot.
The first slot that can hold the item gets it. An empty first hotbar slot wins over a later matching stack with room.
New items go after everything already held. An empty slot in the middle stays unused until the items after it are gone. A bag with a slot limit can count as full while that gap remains.
Choose this only when each item has one reserved slot. If slots are unlimited, choose Append instead.
Why this is asked

Two teams building the same spec can fill the same container differently. That changes what players see and what the game removes later. Rules that score slots, rotate between containers, or use a custom sequence stay in your game's prose.

The game needs an item for one of its own rules. Which matching slot does it take from?

Choices for The game needs an item for one of its own rules. Which matching slot does it take from?
The first matching slot supplies the item. Crafting uses the earliest stack in the bag before later stacks.
The last matching slot supplies the item. Crafting empties the last matching stack before earlier stacks.
The stack that received items most recently supplies them first. Topping up an older potion stack makes it the newest again.
The stack that began first supplies items first. Topping up an old potion stack does not make it newer.
The game never has to choose between matching slots. The player chooses a slot, only one can match, or the container has no slots.
Why this is asked

Automatic use needs a stable choice because it can change which stack remains for the player. A rotating choice over counted amounts belongs in the Ranged value contract.

Which kinds of item can this container hold?

Choices for Which kinds of item can this container hold?
Only listed item types can enter. A potion bag says no to a sword. The sword stays where it was; nothing is destroyed. An empty list means the container accepts nothing.
Every item type may enter once the contract knows how much room it needs. Where slots alone set the limit, an unlisted type — a quest letter no row names — uses one slot per item and cannot be forced out. Under a size budget, it is refused intact until a row names its size.
Each slot has its own item rule. An armour slot accepts a helmet but refuses a potion without destroying it. List every kind that needs a stacking or forced-out rule.

Each item row's slot-rules-declared-in citation is binding. It points to prose that lists every slot and every item kind that slot accepts; an item not accepted there is refused intact.

Why this is asked

A clear item rule prevents silent differences between what the game accepts and what the designer intended. Rules built from another list or a code callback stay in your game's prose.

Numbers and rules2 numbers, 2 rules

Numbers

Each setting takes a number, or a reference to a named value in your tuning. Stay inside any range shown.

Slot count

slot-count

How many slots the container has. Use at least one when slots set capacity. Use zero when this container has no slot limit. If your game stores this number elsewhere, move it here so two copies cannot disagree.

Size budget

size-budget

The largest total size the container holds. Your game chooses the unit, such as kilograms or bulk. Every item uses the same unit. Use zero when size does not limit the container.

Rules

  • slot-count >= 0
  • size-budget >= 0
Lists1 list

Some settings are lists of rows.

A reference points to a number or a rule in your design. For a number, use its address in your tuning. For a rule, use its file and heading, such as 02-mechanics.md#recovery. Each field's description says what it needs.

Items

items

The item types whose rules this contract records. Use one row for each type. For a restricted container, the rows are also the allowed list. An empty list means no listed types. For any-item, the rows plus the unlisted-type defaults fully decide entry, stacking, and forced removal for every type. The validator (the checking tool) checks each row's fields; a reviewer checks that the game's own prose does not give unlisted types a rule different from the defaults.

Each row is: id, slot-rules-declared-in, stack-limit, size, when-forced-out, forced-out-order.

Every field
FieldKindWhen it appearsMeaning
id string Required The item type's name, written as your game names it.
slot-rules-declared-in reference Present when Type restriction is By slot. The place in this game's prose that lists every slot and every item kind it accepts. This citation is the binding rule for the slots. Use the same citation on every row when one section holds the whole list.
stack-limit integer Present when Capacity limit is Slot count or Slots and size or No limit. How many items of this type fit in one slot. It is required whenever the container has slots, including when it never fills. Use one for an item that does not stack, or another positive whole number.
size number Present when Capacity limit is Size budget or Slots and size. How much of the size budget one item uses. Use the same unit as the container's budget. Use zero or more.
when-forced-out choice: never, dropped-nearby, moved-to-another-store, destroyed Required What happens to this item when the container must give items up. It may stay, drop nearby, move to another store, or be destroyed. An item marked to stay is skipped. The container tries other types in their chosen order. It refuses the forced removal if too few items can leave. If destroying an item could make the game impossible to finish, say so and test it in your own rules.
forced-out-order integer Present when row when forced out is Dropped nearby or Moved to another store or Destroyed. The order in which item types leave, lowest number first. Give each movable type a different number. Items of one type leave from its last occupied slot first. The size of the gaps between numbers does not matter.

For builders

Exact mechanism wording and 14 pack tests

Exact mechanism wording

One container holds items. Two items of one type are interchangeable; items differing within a type — wear, charges — are separate types here, or this game's prose. A slot holds one stack: items of one type together.

The container's own order is the order the game's material numbers or lists its slots; first and last below mean first and last in it. A container with no slots has none. Containers where an item occupies a width-and-height footprint, including attaché cases and grid inventories, are outside version 1.

A put offers a type and an amount — a delivery. A take asks for a type and an amount; one larger than the holdings yields what is held and reports the shortfall.

A delivery that fits is accepted whole; one that does not is settled by partial-acceptance, and anything left over by leftover-destination.

A stack never holds more than the stack limit on its type's row. Past it, an amount starts a new stack in a free slot, or counts as not fitting if there is none. Under type-restriction any-item, where slots alone set the limit, a type with no row stacks one item per slot and is unmovable in a forced removal, as though when-forced-out were never; under a size budget an unlisted type has no size, so it is refused intact until a row names its size.

A forced removal is an item leaving without a take: the container shrank, its rules changed, or its owner is gone. Each item row says what becomes of its type and when its turn comes. In a container with slots, the last occupied slot within one type goes first.

One adoption file describes one container; two are two files with their own rows. Moving an item between them is a take then a delivery, and nothing binds them. Rearranging by hand is input. In a container with slots, a sort re-delivers the contents in the order the sort names; each delivery then lands by the fill-order answer. A slotless container's sort changes nothing observable.

The same delivery from the same contents lands the same way; the same take comes from the same slot.

Verification pack

sha256:8f40a54e650b683136852e7a5b47976ebe709f810057f847424d6d8ab3b41b20

The format calls an adoption with its matching pack Checked: the tests travel with it, but a game has not necessarily passed them. Without the pack the adoption is Promised: the builder still owes the chosen behavior.

14 pack tests

Placeholders are filled from the adoption's answers, values, rows, and verification inputs.

a delivery that fits is accepted whole

delivery-that-fits

scenarioonce

Applies for every adoption

A delivery Instance has room for is accepted whole: the container gains exactly what was offered, keeps everything it already held, and nothing is left over to account for. This is the ordinary case, written down so that every other test below reads as a departure from it.

Given

the Instance container with room for the whole of the delivery below

When
  • a delivery offers n items of a type Instance accepts
Then
  • Instance holds exactly n more items of that type than it did
  • nothing already in Instance is removed, destroyed, or changed into another type
  • nothing is handed back, dropped, or destroyed
Diagnostics
  • Instance-contents-before-after
a delivery that does not fit is refused whole

refuses-whole

scenarioonce

Applies when Partial acceptance is All or nothing.

A delivery larger than the room available leaves Instance untouched. Partial delivery is the failure this answer rules out: even the items that would have fitted stay out, and the delivery says so. Whether the action that offered them still occurs is decided in this game's own prose, not here.

Given

the Instance container with room for fewer than n items of a type it accepts

When
  • a delivery offers n items of that type
Then
  • Instance holds exactly what it held before
  • not one of the n items goes in — not even the ones that would have fitted
  • the delivery reports that it did not happen
Diagnostics
  • Instance-contents-before-after
  • Instance-delivery-report
a delivery that does not fit is split

takes-what-fits

scenarioonce

Applies when Partial acceptance is Fill what fits.

A delivery larger than the room available fills Instance as far as it goes and reports what it could not take. The part that did not fit is Bind leftover phrase. Where that part ends up next, and who is responsible for it after that, is decided in this game's own prose.

Given

the Instance container with room for m items of a type it accepts, where m is more than zero and less than n

When
  • a delivery offers n items of that type
Then
  • Instance holds exactly m more items of that type
  • the remaining n minus m items are Bind leftover phrase
  • the delivery reports n minus m as the number it could not take
Diagnostics
  • Instance-contents-before-after
  • Instance-delivery-report
the container is full when its slots are full

slots-are-the-limit

scenarioonce

Applies when Capacity limit is Slot count or Slots and size.

This container is full when every slot stated by the value at Value cite slot count is in use and none of them can take more of what is being offered. This test cites that value and restates the number nowhere.

Given

the Instance container with all Value cite slot count slots in use and no slot in use able to take more of the type below

When
  • a delivery offers one item of that type
Then
  • Instance takes none of it
  • the number of slots in use is still the number stated by Value cite slot count
Diagnostics
  • Instance-slot-occupancy
  • Instance-delivery-report
the container is full when its size budget is spent

size-is-the-limit

scenarioonce

Applies when Capacity limit is Size budget or Slots and size.

This container is full when the next item's size would take the total past the value at Value cite size budget. Sizes come from the item rows and the budget from that value; this test restates neither number.

Given

the Instance container holding items whose sizes add up to less than Value cite size budget

When
  • a delivery offers items whose sizes would take the total past Value cite size budget
Then
  • the sizes of the items Instance holds never add up to more than Value cite size budget
  • each item's size is the size written on its own type's row, in the unit this game's prose names for the budget
Diagnostics
  • Instance-size-total
  • Instance-delivery-report
nothing is ever refused for want of room

never-refuses

scenarioonce

Applies when Capacity limit is No limit.

This container has no capacity to run out of, so no delivery can fail for want of room. That is a recorded decision and not an omission: a reader who finds no limit here is being told there is none, rather than left to guess.

Given

the Instance container holding any amount at all

When
  • a delivery offers any number of items of a type Instance accepts
Then
  • every offered item goes in
  • nothing is handed back, dropped, or destroyed for want of room
  • no amount of prior contents changes the answer
Diagnostics
  • Instance-contents-before-after
an accepted item lands in the declared slot

fill-order-holds

scenarioonce

Applies when Fill order is Top up then first empty or First slot that fits or Append.

An item Instance accepts lands Bind fill phrase. The rule is repeatable: the same delivery from the same starting contents lands in the same slot every time. Slot numbering is the numbering identified by this adoption's verification scope. Six externally authored games answer this question six different ways and none of them writes the answer down — this line is the whole reason the question is asked.

Given

the Instance container holding some of a type in one slot, with at least one other slot free and at least one slot freed by an earlier removal, its slots numbered as Inputs scope sets out

When
  • a delivery offers one more item of that type
Then
  • the item lands Bind fill phrase
  • the same delivery made again from the same starting contents lands in the same slot
Diagnostics
  • Instance-slot-occupancy
  • Instance-fill-trace
the game takes from the declared slot

draw-order-by-position

scenarioonce

Applies when Draw order is First in order or Last in order.

A take the game makes on its own comes Bind draw phrase, repeatably, against the slot numbering at Inputs scope. Five externally authored games answer this question and answer it five different ways, no two alike; one of them has written its answer down, and only after its own users filed a bug about the silence. If Instance adopts nothing else from this contract, it should adopt this line.

Given

the Instance container holding the same type in at least two slots, its slots numbered as Inputs scope sets out

When
  • the game takes fewer of that type than Instance holds, on its own rather than at a player's direction
Then
  • the items come Bind draw phrase
  • the same take made again from the same starting contents comes from the same slot
  • no slot other than the one the rule names is touched while that one can still supply the take
Diagnostics
  • Instance-slot-occupancy
  • Instance-take-trace
the game takes by when the stack arrived

draw-order-by-arrival

scenarioonce

Applies when Draw order is Newest first or Oldest first.

A take the game makes on its own comes Bind arrival phrase. Position is not the rule here: Instance has to remember when its stacks received items, and the answer says plainly whether topping a stack up counts as arriving. That clause is where two games with the same one-word answer quietly differ.

Given

the Instance container holding the same type in at least two stacks that received items at different times, one of them topped up after the other was made

When
  • the game takes fewer of that type than Instance holds, on its own rather than at a player's direction
Then
  • the items come Bind arrival phrase
  • where each stack sits makes no difference to which one supplies the take
  • the same take made again from the same starting contents and the same arrival history comes from the same stack
Diagnostics
  • Instance-stack-arrival-log
  • Instance-take-trace
Row.id stacks no higher than its limit

stack-limit-holds

scenarioper items row

Applies when Capacity limit is Slot count or Slots and size or No limit.

A stack of Row.id never holds more than Row.stack limit. The limit belongs to the item type rather than to Instance, which is how every surveyed game that stacks at all does it, so the same number holds in every container that carries Row.id.

Given

the Instance container with one slot holding Row.stack limit of Row.id

When
  • a delivery offers one more Row.id
Then
  • that slot still holds exactly Row.stack limit of Row.id
  • the extra one starts a new stack in a free slot; where this container's slots can run out and none is free, it counts as not fitting and is settled by the partial-acceptance answer
Diagnostics
  • Instance-slot-occupancy
  • Instance-delivery-report
a type the list does not name is refused

unlisted-type-refused

scenarioonce

Applies when Type restriction is Only these.

This container holds only the types its item list names. A delivery of anything else is refused whole even when there is room, and the refusal costs the refused item nothing — being turned away is not the same as being thrown away. Where the item list is this game's whole catalogue, no unlisted type exists to offer and this test has no reachable case: a builder records it as unreachable rather than inventing a type to refuse.

Given

the Instance container with room to spare

When
  • a delivery offers an item of a type the item list does not name
Then
  • Instance takes none of it, however much room it has
  • nothing already in Instance changes
  • the refused item is not destroyed
Diagnostics
  • Instance-contents-before-after
  • Instance-delivery-report
Row.id leaves Instance in the declared way

forced-out-holds

scenarioper items row

Applies when row when forced out is Dropped nearby or Moved to another store or Destroyed.

When Instance is made to give items up, Row.id is Bind fate phrase, and it goes at position Row.forced out order in the declared order. When the container has slots and Row.id sits in more than one slot, the last of those slots empties first — the row number orders the types, and that rule orders the slots inside one type. Of the five surveyed games that can force items out, three declare the switch that turns it on and none declares which item goes or where it lands — that gap is what this row closes.

Given

the Instance container holding more than one Row.id and, when it has slots and more than one slot is needed for that many, holding that type in more than one slot, together with any types carrying lower or higher forced-out order numbers

When
  • Instance is made to give items up without a take
Then
  • Row.id is Bind fate phrase
  • Row.id goes at position Row.forced out order in the order: any type present with a lower number has gone before it, and any type present with a higher number goes after it
  • when the container has slots and Row.id sits in more than one slot, the last occupied slot of that type goes first and the container works back through its own order
  • no type that did not have to leave leaves
Diagnostics
  • Instance-forced-removal-log
  • Instance-contents-before-after
Row.id is never given up by Instance itself

never-forced-out

scenarioper items row

Applies when row when forced out is Never.

Nothing this container does on its own removes Row.id from it: when Instance has to shed items, this type is passed over, and only when shedding everything that can go is still not enough does Instance refuse the change outright. A player choosing to drop, sell, or destroy Row.id is a different thing entirely and is governed by this game's own rules, not by this line. Where this game has no way to shrink Instance or change its rules while it runs, this test has no reachable case: a builder records it as unreachable rather than inventing a trigger for it.

Given

the Instance container holding Row.id at a moment when something would leave it holding more than it can — its slots shrank, or its rules changed

When
  • that change is attempted
Then
  • no Row.id leaves Instance: the type is passed over while types that can go are given up in their declared order
  • if giving up every type that can go is still not enough, Instance refuses the change
  • no Row.id is dropped, moved to another store, or destroyed by Instance itself
Diagnostics
  • Instance-forced-removal-log
  • Instance-contents-before-after
the limit holds after every delivery and take

capacity-holds

generalonce

Applies when Capacity limit is Slot count or Size budget or Slots and size.

For every delivery and take in this adoption's verification scope — Inputs scopeBind limit clause. The scope and seeds are supplied by this adoption through its verification inputs; the rule being checked is the one this container's own capacity answer entitles it to assert.

Diagnostics
  • Instance-contents-trace
  • first-violating-delivery-or-take
Holds

after every delivery and every take, Bind limit clause

Seeds

Inputs seeds

Scope

Inputs scope

Use this contract in your game ↑