Appearance
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.itemId.key: properties are readable as $item.iron_knife.damage. These are initialized by (item-define:). Read a property dynamically (e.g. in a loop) with (get: $item, "iron_knife", "damage").
item-define
(item-define: "id", stackable, key, value, ...) · (item-define: "id", unique, key, value, ...)
Defines an item. The second argument is either stackable or unique:
stackable: tracked by quantity.(add: $inv, "gold_coin", 50)gives you 50 coins, all identical.$inv.gold_coinis the count.unique: the single item by its id; there is only ever one. If you addsteel_sword, your inventory holdssteel_sword($inv.steel_swordis1). Adding it again leaves it at one. If you want two similar-but-distinct items, define two unique items (rusty_swordandbroken_rusty_sword).
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: there is exactly one of each
(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)A unique item can carry mutable per-item state (durability, charges, and the like) that follows it between inventory, containers, and equipment. It's keyed by the item's own id and falls back to the template property when unset, so a fresh item reads its defaults until something changes it.
get (items)
(get: $item, "id", "key")
Expression macro. Returns the value of a property for an item, like the mutable per-item state. Returns undefined if the property doesn't exist, never throws. This matters 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.itemId: item counts are readable inline: (if: $inv.beer >= 2)[...], (if: $inv.steel_sword)[...]. Updated automatically by (add:) and (remove:). A unique item is the single item by its id, so $inv.steel_sword is 1 when carried and 0 otherwise, the same $inv.itemId shape as stackable items.
add (inventory)
(add: $inv, "itemId", quantity?)
Adds an item to the carry inventory. Quantity defaults to 1.
- Stackable: bumps the count for that id.
- Unique: there is only ever one, so the inventory holds exactly one regardless of
quantity. Adding it again is a no-op.
ana
(add: $inv, "beer", 2)
(add: $inv, "lockpick", 3)
// Unique: you have it, and $inv.iron_knife becomes 1
(add: $inv, "iron_knife")remove (inventory)
(remove: $inv, "itemId", quantity?)
Removes an item by its id. Does nothing if the item isn't present (no error). Quantity defaults to 1. Returns false without removing anything if fewer than quantity are carried.
ana
(remove: $inv, "key", 1)
(remove: $inv, "beer", 1)
(remove: $inv, "iron_knife") // the one iron_knife leaves the inventorydestroy
(destroy: target, ...)
Permanently removes an item from play. Unlike (remove:) (which, for equipment, returns items to inventory), (destroy:) deletes. When the last copy of a unique item leaves play, its mutable per-item state is cleared too, so a later re-acquired item of the same id starts from template defaults. Polymorphic across the three storage namespaces:
| Form | Effect |
|---|---|
(destroy: $inv, "id", qty?) | Remove qty (default 1) from carry inventory |
(destroy: $equip.slot) / (destroy: $equip.slot, "id") | Unequip and delete; does not return to inventory |
(destroy: $container.id, "itemId", qty?) | Remove from a container and delete |
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 consumedUnequip 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 (inventory)
(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).
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 the iron knife.
]count (inventory)
(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".
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") // 1 if the unique knife is carried, else 0ids (inventory)
(ids: $inv)
Expression macro. Returns an array of all item IDs currently in the inventory.
ana
(each: _id in (ids: $inv))[
You are carrying (get: $item, _id, "name").
]filter (inventory)
(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 and the contains family
(contains: collection, value)
Expression macros. Membership checks that work polymorphically over a plain array variable, $inv, or a $container.id. They read more clearly than chained contains operators.
| Macro | Returns |
|---|---|
(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 id 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 id; use (get: $equip.ring) for the full list.
equip-define
(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 simultaneouslyadd (equipment)
(add: $equip.slotName, "id")
Equips an item to a slot by its id. 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 an item that was never in inventory puts it on the slot directly.
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") // equips; removes from inventory if carried; displaces old weapon if full
(add: $equip.armor, "leather_jacket")remove (equipment)
(remove: $equip.slotName) · (remove: $equip.slotName, "id")
Unequips from a slot. One argument removes all items from the slot. Two arguments removes only the named item. Removed items return to carry inventory (mirroring how equipping into a full slot displaces the old item back). Use (destroy: $equip.slot) to remove from play instead.
ana
(set: _was to (get: $equip.weapon))
(remove: $equip.weapon)
// Remove a specific item from a multi-capacity slot:
(remove: $equip.ring, "signet_ring")has (equipment)
(has: $equip.slotName) · (has: $equip.slotName, "id")
Expression macro. Returns true if the slot has any item equipped (one argument), or if the specific item id 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"))[
The iron sword is in your hand.
]get (equipment)
(get: $equip.slotName) / (get: equip, slotExpr)
Expression macro. Returns an array of item ids 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: _id in (get: $equip.ring))[
(get: $item, _id, "name") glints on your finger.
]
// Dynamic slot inside a loop over (equip-slots:)
(each: _slot in (equip-slots:))[
(set: _id to (first: (get: equip, _slot)))
]The dynamic form is also accepted by (has: equip, slotExpr) and (remove: equip, slotExpr).
equip-slots
(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, (first: (get: equip, _slot)), "name")
]A ready-made, restyleable equipment screen built from
(equip-slots:)lives intemplates/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
(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)
(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)
(remove: $container.containerName, "itemId", quantity?)
Removes an item from a container.
ana
(remove: $container.apartment_closet, "winter_coat", 1)has (container)
(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)
(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)
(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)
(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.
]