Skip to content

Items, Inventory, Equipment & Containers

Defining item templates and moving instances between carry inventory, equipment slots, and world containers.

Items

Item definitions are the catalog of everything that can exist in the game world. Define templates in GameInit.

$variable path: $item.templateId.key: template properties are readable as $item.iron_knife.damage. These are initialized by (item-define:) and are read-only at runtime. Per-instance overrides on unique items still require (get: $item, uid, "key").


(item-define: "id", stackable, key, value, ...)

(item-define: "id", unique, key, value, ...)

Defines an item template. The second argument is either stackable or unique:

  • stackable: items tracked by quantity. (add: $inv, "gold_coin", 50) gives you 50 coins, all identical. $inv stores the count under the template id itself.
  • unique: each item is an individual instance with its own ID and potentially different properties. $inv never stores a unique item under its bare template id; every copy that enters $inv is a real instance (uid). (add: $inv, "id", quantity?) creates instance(s) automatically; (create: $item, "id") spawns one explicitly when you need the uid up front (e.g. to set per-instance overrides before adding it to inventory).

Properties are flat key-value pairs after the mode. Any key names you choose.

ana
// Stackable — tracked by quantity
(item-define: "beer", stackable, name, "Beer", type, "consumable", weight, 1, value, 5)
(item-define: "gold_coin", stackable, name, "Gold Coin", type, "currency", weight, 0, value, 1)
(item-define: "lockpick", stackable, name, "Lockpick", type, "tool", weight, 0, value, 8)

// Unique — each instance is tracked individually
(item-define: "iron_knife", unique, name, "Iron Knife", type, "weapon", damage, 8, weight, 2, value, 25)
(item-define: "missing_persons_report", unique, name, "Missing Persons Report", type, "document", weight, 0, value, 0)

(create: $item, "templateId")

Expression macro. Spawns a new unique instance and returns its instance ID. Only valid for unique items; (create:) cannot be used with stackable items. Stackable items are tracked by count and do not have individual instances; use (add: $inv, "itemId", quantity) for those.

ana
// Define the item as unique first:
(item-define: "enchanted_blade", unique, name, "Enchanted Blade", damage, 15)

// Then create an instance and add it to inventory:
(set: _uid to (create: $item, "enchanted_blade"))
(add: $inv, _uid)

Instance IDs are generated automatically (e.g., "iron_knife_001"). Store them in variables to reference the specific instance later.

ana
(set: _myKnife to (create: $item, "iron_knife"))
(add: $inv, _myKnife, 1)

For the common case (handing the player a unique item with no special overrides), (add: $inv, "iron_knife") does this (create:) + (add:) pair for you. Reach for (create:) explicitly when you need the uid immediately, e.g. to call (set: $item, uid, "key", value) before the item reaches inventory.


(get: $item, uid, "key")

Expression macro. Returns the value of a property from an item template or instance. Returns undefined if the property doesn't exist on the item or NPC, never throws. This matters when used in (if:) conditions: (if: (get: $item, _id, "poisoned")) is falsy when poisoned is not defined.

ana
(if: (get: $item, "iron_knife", "damage") > 10)[
    This is a serious weapon.
]

(set: _weapons to (filter: $inv.type is "weapon"))
(each: _id in _weapons)[
    (get: $item, _id, "name") — damage: (get: $item, _id, "damage")
]

Inventory

The carry inventory: items your character has on their person. For items that are worn, use Equipment. For items stored at a location, use Containers.

$variable path: $inv.templateId: stackable item counts are readable inline: (if: $inv.beer >= 2)[...]. Updated automatically by (add:) and (remove:). Unique items are tracked by instance UID ($inv.item_2), but (has:), (count:), (remove:), and (destroy:) also accept the template id for a unique item and transparently resolve it to a carried instance; see (add: $inv, "itemId", quantity?) below.


(add: $inv, "itemId", quantity?)

Adds an item to the carry inventory. Quantity defaults to 1.

  • Stackable templates: bumps the stack count for that template id.
  • Unique templates: creates quantity new instances (default 1) via (create:) and adds each instance's uid to $inv. $inv never ends up holding a bare unique template id.
  • Instance uid (from (create:)): adds that specific instance, unchanged from before.
ana
(add: $inv, "beer", 2)
(add: $inv, "lockpick", 3)

// Unique template — creates a new instance automatically:
(add: $inv, "iron_knife")

// Or create the instance yourself first if you need to set overrides:
(set: _knife to (create: $item, "iron_knife"))
(set: $item, _knife, "damage", 12)
(add: $inv, _knife, 1)

(remove: $inv, "itemId", quantity?)

Removes an item. Does nothing if the item isn't present (no error). Quantity defaults to 1.

For a unique template id, resolves to quantity matching carried instance(s) and removes those; the instances themselves still exist (use (destroy:) to free them). Returns false without removing anything if fewer than quantity matching instances are carried.

ana
(remove: $inv, "key", 1)
(remove: $inv, "beer", 1)
(remove: $inv, "iron_knife")   // removes one carried iron_knife instance

(destroy: target, ...)

Permanently removes an item from play. Unlike (remove:) (which, for equipment, returns items to inventory), (destroy:) deletes, and for unique items it also frees the underlying instance from the item system. Polymorphic across the three storage namespaces:

FormEffect
(destroy: $inv, "id", qty?)Remove qty (default 1) from carry inventory; delete the instance if unique
(destroy: $equip.slot) / (destroy: $equip.slot, uid)Unequip and delete; does not return to inventory
(destroy: $container.id, "itemId", qty?)Remove from a container and delete

For a unique template id, (destroy: $inv, "id", qty?) resolves to qty matching carried instance(s), removes them from $inv, and frees each instance from the item system. Returns false without changing anything if fewer than qty matching instances are carried.

Destroying something that isn't present is a safe no-op (returns false).

ana
(destroy: $inv, "cursed_ring")           // consume a one-use cursed item for good
(destroy: $equip.weapon)                 // the blade shatters — gone, not unequipped
(destroy: $container.altar, "offering")  // the offering is consumed

Unequip vs. destroy. (remove: $equip.slot) returns the item to carry inventory (the safe default, mirroring how equipping into a full slot displaces the old item back). Use (destroy: $equip.slot) only when the item should leave the game entirely.


(has: $inv, "itemId")

(has: $inv, "itemId", count)

Expression macro. Returns true if the player has at least 1 of the item (one argument), or at least count of the item (two arguments).

For a unique template id, this checks how many matching instances are currently carried, so (has: $inv, "iron_knife") works without knowing the generated instance uid.

ana
(if: (has: $inv, "key"))[
    The lock clicks open.
]
(else:)[
    The door won't budge.
]

(if: (has: $inv, "lockpick", 3))[
    You have enough lockpicks for the job.
]

(if: (has: $inv, "iron_knife"))[
    You're carrying at least one iron knife.
]

(count: $inv, "itemId") / (count: array)

Expression macro. With a namespace and an id, returns the quantity held (0 if not present). With a single array argument (a variable or any expression that evaluates to an array), returns the array's length, so (count:) is the one verb for "how many". (arr-count:) is a retained alias of the array form.

For a unique template id, returns how many matching instances are currently carried.

ana
(if: (count: $inv, "coin") >= 5)[
    You have enough to bribe him.
]

(count: $player.events)        // array length
(count: (ids: $npc))           // how many NPCs exist
(count: $inv, "iron_knife")    // how many iron knives are carried

(ids: $inv)

Expression macro. Returns an array of all template IDs currently in the inventory.

ana
(each: _id in (ids: $inv))[
    You are carrying (get: $item, _id, "name").
]

(filter: $inv.property is value [, $inv.property2 is value2 ...])

Expression macro. Returns an array of item IDs in the carry inventory where all predicates match (AND). Multiple predicates are supported.

ana
(set: _weapons to (filter: $inv.type is "weapon"))
(each: _id in _weapons)[
    You have a (get: $item, _id, "name") on you.
]

// Multi-predicate: heavy weapons only
(set: _heavy to (filter: $inv.type is "weapon", $inv.weight > 5))

(contains: collection, value) and the contains family

Expression macros. Membership checks that work polymorphically over a plain array variable, $inv, or a $container.id. They replace the old (inv-has-count:) and read more clearly than chained contains operators.

MacroReturns
(contains: coll, "v")true if the collection holds "v"
(contains-all: coll, "a", "b", …)true if every listed value is present
(contains-any: coll, "a", "b", …)true if at least one is present
(contains-count: coll, "a", "b", …)how many of the listed values are present (0…N)
ana
// True if the player has all three pieces of the set:
(if: (contains-all: $inv, "bronze_gloves", "bronze_chest", "bronze_helm"))[
    You're wearing a full set of bronze armor.
]

// Cleaner than: (if: $player.events contains "a" and $player.events contains "b")
(if: (contains-all: $player.events, "met_kyle", "found_note"))[...]

// Use as a count expression in (achieve-define:):
(achieve-define: "bronze_warrior", name, "Bronze Warrior", desc, "Collect the bronze armor set",
  count, (contains-count: $inv, "bronze_gloves", "bronze_chest", "bronze_helm"),
  tiers, 1, 2, 3)

For a single value, the contains operator ($inv.beer contains … is not valid, but $player.events contains "x" is) remains the terse choice inside conditions; the macros shine for multi-value and namespace checks.

Equipment

Slotted worn/equipped items. Each slot holds one item (or multiple, for ring-style slots). Define slots in GameInit.

All macros in this section accept $equip.slotName as an alias for the bare equip.slotName form; both are identical in behavior.

$variable path: $equip.slotName: the UID of the first equipped item, or "" if the slot is empty. Readable inline: (if: $equip.weapon != "")[...]. For multi-capacity slots, $equip.ring holds only the first UID; use (get: $equip.ring) for the full list.


(equip-define: slotName)

(equip-define: slotName, capacity)

Registers an equipment slot. Call in GameInit. The optional second argument sets the capacity (default 1) for slots that allow multiple items.

ana
(equip-define: weapon)
(equip-define: armor)
(equip-define: ring, 2)     // can hold 2 rings simultaneously

(add: $equip.slotName, uid)

Equips an item to a slot. Equipping is a transfer, not a copy: if the item is currently in carry inventory, it is removed from inventory as it goes onto the slot (so it can't exist in both places). Equipping a freshly (create:)d item that was never in inventory is unaffected.

If uid names a unique item template (not an instance), a new instance is created and equipped; $equip never holds a bare unique template id, matching (add: $inv, "id").

If the slot is at capacity, the oldest equipped item (equipped[0]) is automatically displaced back to carry inventory, and a feedback entry (items category) reports "Unequipped: <item name>". So equipping a carried item into a full slot is a clean swap: the new item leaves inventory, the old one enters it. For multi-capacity slots (e.g. ring slots), only one item is displaced per call.

(remove: $equip.slot) reverses this: it returns the item to inventory; (destroy: $equip.slot) removes it from play entirely.

ana
(add: $equip.weapon, "iron_sword_001")   // equips; removes from inventory if carried; displaces old weapon if full
(add: $equip.armor, "leather_jacket_001")

(remove: $equip.slotName)

(remove: $equip.slotName, uid)

Unequips from a slot. One argument removes all items from the slot. Two arguments removes only the specified item. Removed items are not automatically moved to inventory.

ana
(set: _was to (get: $equip.weapon))
(remove: $equip.weapon)

// Remove a specific item from a multi-capacity slot:
(remove: $equip.ring, "signet_ring_001")

(has: $equip.slotName)

(has: $equip.slotName, uid)

Expression macro. Returns true if the slot has any item equipped (one argument), or if the specific UID is equipped (two arguments).

ana
(if: (has: $equip.weapon))[
    Your hand rests on the grip.
]
(else:)[
    You're unarmed.
]

(if: (has: $equip.weapon, "iron_sword_001"))[
    The iron sword is in your hand.
]

(get: $equip.slotName) / (get: equip, slotExpr)

Expression macro. Returns an array of item UIDs currently equipped in that slot. Empty array if nothing is equipped. The first form takes a static slot; the second, dynamic form (bare equip + a slot expression) is for loops where the slot name is in a variable.

ana
(each: _uid in (get: $equip.ring))[
    (get: $item, _uid, "name") glints on your finger.
]

// Dynamic slot inside a loop over (equip-slots:)
(each: _slot in (equip-slots:))[
    (set: _uid to (arr-first: (get: equip, _slot)))
]

The dynamic form is also accepted by (has: equip, slotExpr) and (remove: equip, slotExpr).


(equip-slots:)

Expression macro. Returns an array of all defined equipment slot names (the ones you registered with (equip-define:)). Loop over it to build an equipment screen.

ana
(each: _slot in (equip-slots:))[
    (proper: _slot): (get: $item, (arr-first: (get: equip, _slot)), "name")
]

Replaces the old (equip-display:) render macro. A ready-made, restyleable equipment screen built from (equip-slots:) lives in templates/equipment.ana; copy it and customize.

Containers

Named storage locations: wardrobes, car trunks, shop inventories, lockboxes, NPC inventories.

All macros in this section accept $container.id as an alias for the bare container.id form; both are identical in behavior.

$variable path: $container.containerId.templateId: item counts readable inline: (if: $container.chest.beer >= 1)[...]. Updated automatically by (add:) and (remove:).


(container-define: "containerName")

Creates a named container. Call in GameInit or wherever you establish a location.

ana
(container-define: "apartment_closet")
(container-define: "car_trunk")
(container-define: "shop_goods")

(add: $container.containerName, "itemId", quantity?)

Adds an item to a $container. Quantity defaults to 1.

ana
(add: $container.apartment_closet, "winter_coat", 1)
(add: $container.shop_goods, "lockpick", 5)

(remove: $container.containerName, "itemId", quantity?)

Removes an item from a container.

ana
(remove: $container.apartment_closet, "winter_coat", 1)

(has: $container.containerName, "itemId")

(has: $container.containerName, "itemId", count)

Expression macro. Returns true if the container holds at least count of the item (default 1).

ana
(if: (has: $container.apartment_closet, "winter_coat"))[
    Your winter coat hangs in the closet.
]

(count: $container.containerName, "itemId")

Expression macro. Returns the quantity of an item in the container.

ana
(if: (count: $container.shop_goods, "lockpick") > 0)[
    The shopkeeper has lockpicks in stock.
]

Moving items between containers and inventory:

ana
(if: (has: $container.apartment_closet, "winter_coat"))[
    (remove: $container.apartment_closet, "winter_coat", 1)
    (add: $inv, "winter_coat", 1)
    (notify: "You grab your winter coat.")
]

(remove: $inv, "spare_key", 1)
(add: $container.apartment_closet, "spare_key", 1)
(notify: "You leave the spare key in the closet.")

(ids: $container.containerName)

Expression macro. Returns an array of all item IDs in a container.

ana
(each: _id in (ids: $container.shop_goods))[
    The shop has (get: $item, _id, "name") in stock.
]

(filter: $container.containerName.property is value)

Expression macro. Returns an array of item IDs in the container where that property matches.

ana
(set: _potions to (filter: $container.shop_goods.type is "potion"))
(each: _id in _potions)[
    A (get: $item, _id, "name") sits on the shelf.
]

(set: _greenJackets to (filter: $container.wardrobe.color is "green"))
(each: _id in _greenJackets)[
    (get: $item, _id, "name") hangs on the rack.
]