BELOW THE IRON Modding guide

Items#

This page lets you add or change what a player wears, crafts, enchants and drinks: affixes, set bonuses, forge catalysts, potions and the glyphs that draw equipment. Every example is JSON only.

What you can change#

All files are in public/config/. Visible text is never written inline: you write a text key (in capitals, like AFFIX_NAME_OF_THE_GOURMET) and add it to every language file in public/config/locales/.

Affixes and their tiers#

An affix is a named bonus on an item, such as "of the Gourmet". Its family points at an effect, which is what the bonus does. Add affixes to affixes in equipment.json.

Goal: a ring or amulet suffix that reduces the hunger you use.

{
  "id": "of-the-gourmet",
  "label": "AFFIX_NAME_OF_THE_GOURMET",
  "position": "suffix",
  "family": "satiety",
  "kinds": ["ring", "amulet"],
  "tiers": [[3, 5], [5, 8], [8, 12], [12, 16]],
  "weight": 5,
  "minLevel": 1,
  "group": "satiety"
}
In game

Rings and amulets can drop with "of the Gourmet". A tier 1 item rolls a value from 3 to 5; a tier 4 item rolls 12 to 16. The item's quality caps the tier it can reach.

Affix effects#

An effect holds modifiers (numbers that change) and listeners (reactions to an event). In both, "$value" stands for the number the affix rolled. The game ships 16 effects in affixEffects.

Goal: a "siphon" effect: when a projectile spell hits a creature, you get mana back.

{
  "siphon": {
    "text": "AFFIX_EFFECT_SIPHON",
    "listeners": [
      {
        "id": "siphon-mana",
        "on": "spell.hit",
        "when": { "kind": "projectile" },
        "action": "restoreMana",
        "params": { "amount": "$value" },
        "cooldown": 2
      }
    ]
  }
}

Write an affix with "family": "siphon", as above. Every effect also needs exactly one engraving reagent, in engrave.reagentByFamily of crafting.json:

{ "siphon": "pearl" }
In game

An item with the affix returns mana equal to its value when a projectile spell hits, at most every 2 turns. Players engrave the affix with pearl.

Synergies#

A synergy rewards a named item that carries affixes from several families. Add it to synergies in equipment.json. Its effect is always on; its special is a listener, or a chance modifier.

Goal: a critical hit heals you.

{
  "id": "bloodthirst",
  "label": "SYNERGY_BLOODTHIRST",
  "requires": ["damage", "drain"],
  "priority": 5,
  "effect": {
    "text": "SYNERGY_BLOODTHIRST_EFFECT",
    "modifiers": [{ "hook": "hit.drain", "op": "add", "value": 1 }]
  },
  "special": {
    "text": "SYNERGY_BLOODTHIRST_SPECIAL",
    "announce": "SYNERGY_BLOODTHIRST_TRIGGERED",
    "on": "hit.dealt",
    "when": { "critical": true },
    "action": "heal",
    "params": { "amount": "$power" },
    "power": 2
  }
}
In game

An item with a damage affix and a drain affix gains +1 life drain on every hit. Each critical hit also heals 2 life (4 on an exceptional item) and writes the announcement to the log.

Set bonuses#

A set bonus applies when the player wears enough pieces of a set. Each threshold in a set's thresholds list (in sets.equipmentSets of equipment.json) has its own modifiers and listeners.

Goal: a third bonus for the 4-piece "lanterniers" set.

{
  "pieces": 3,
  "modifiers": [
    { "hook": "hunger.cost", "op": "mul", "value": 0.9 },
    { "hook": "attack.accuracy", "op": "add", "value": 1, "when": { "target": "lit" } }
  ],
  "listeners": [
    {
      "on": "floor.entered",
      "action": "revealTraps",
      "params": { "radius": 6 }
    }
  ]
}
In game

With 3 pieces worn, hunger costs 10 % less, attacks on lit targets gain +1 accuracy, and each new floor reveals traps within 6 cells. The 2-piece bonus still applies.

Enchantments#

An enchantment is a permanent power the player writes on an item with a reagent. Its behaviour is an enchantment mechanic in enchantments.mechanics of equipment.json. There, "$value" is the strength of the inscription.

Goal: every third hit sends an arcane wave around you.

{
  "wave-third": {
    "text": "ENCHANTING_EFFECT_WAVE_THIRD",
    "listeners": [
      {
        "id": "third-blow-wave",
        "on": "hit.dealt",
        "action": "wave",
        "params": { "every": 3, "element": "arcane", "amount": "$value" }
      }
    ]
  }
}
In game

On a weapon with this enchantment, every third hit deals arcane damage to all adjacent enemies.

Attach the mechanic to a reagent in enchanting.permanentEffects of crafting.json: "resonance": { "base": 1, "step": 34, "weapon": "wave-third", "armor": "shock-recharge" }. Each reagent names one mechanic for weapons and one for armor. A weapon mechanic can only listen to hit.dealt; an armor mechanic to hit.taken or turn.started.

To share an existing affix effect, write only { "affixEffect": "mana" } for the mechanic. Do not add a second text, modifiers or listeners beside that reference. Mana and light use their shared description with {value}; direct mechanic descriptions use {p0}. Add new text keys to all nine language files. In this context only $value is available, never $skill.

Forge catalysts#

A catalyst is an item the forge burns to help one crafting. Add it to craftingCatalysts in resources.json.

Goal: +30 forge skill and one gold coin per crafting, using two units each time.

{
  "id": "moon-dust",
  "name": "WORLD_MOON_DUST",
  "symbol": "*",
  "color": "#abcdef",
  "units": 2,
  "modifiers": [{ "hook": "craft.skill", "op": "add", "value": 30 }],
  "listeners": [{ "on": "item.crafted", "action": "giveGold", "params": { "amount": 1 } }]
}
In game

Crafting with moon dust uses 2 units, rolls quality as if forge skill were 30 higher, and pays 1 gold when the item is made. Outside that crafting it does nothing.

Item kinds and consumables#

An item kind is a family of objects (potion, torch, tome) declared in itemKinds of items.json. It says whether items are consumable, which forge they need and what they do when used. A recipe link in crafting.json points to its output. The base in equipment.json or the recipe item in items.json declares the kind.

Goal: a potion that restores mana, sold by merchants and listed under its own filter.

First the filter, added to itemFilters:

{
  "mana-draught": { "label": "GAME_MANA_ELIXIR", "inventoryOrder": 20, "craftOrder": 20, "tieBreakOrder": 20 }
}

Then the kind:

{
  "mana-draught": {
    "consumable": true, "equippable": false,
    "effects": [{ "action": "restoreMana", "params": { "amount": "$power" } }],
    "needsForge": true, "craftSkill": "alchemy", "namedByRecipe": true,
    "merchantStock": 1, "alwaysStocked": false,
    "filter": "mana-draught",
    "description": { "category": "INVENTORY_DETAIL_CONSUMABLE", "text": "MANA_ELIXIR_DESCRIPTION" },
    "statLabel": { "heading": "INVENTORY_DETAIL_RESTORATION", "value": "ITEM_EFFECT_MANA" },
    "protection": false, "sheetGroup": null
  }
}

Then the item itself, added to recipeItems in items.json, and the recipe that makes it, added to recipes in crafting.json. Both are shown together here:

{
  "item": {
    "id": "mod-mana-draught", "kind": "mana-draught",
    "name": "GAME_MANA_ELIXIR", "icon": "!", "power": 15, "lore": "MANA_ELIXIR_DESCRIPTION"
  },
  "recipe": { "id": "mod-mana-draught", "output": { "item": "mod-mana-draught" }, "cost": { "pearl": 1, "root": 1 } }
}
In game

The player crafts the draught from a pearl and a root and finds it under its own filter. Drinking it restores 15 mana: $power is the power of that item.

Effects and use#

An effects list is a series of actions run in order. The optional use block says how the item is aimed: scope is self, adjacent, direction or item, with a range.

{
  "scope": "direction", "range": 5,
  "effects": [{ "action": "noise", "params": { "intensity": 2 } }]
}
In game

The player aims up to 5 cells away. A noise of intensity 2 is made in the target cell, and nearby creatures may hear it.

A modifier on consumable.amount changes what a consumable restores. Add action to a condition to target one action:

{
  "hook": "consumable.amount", "op": "add", "value": 2,
  "when": { "kind": "potion", "action": "restoreMana" }
}
Advanced: found objects and reagent use

An object with no recipe goes in objects of items.json. A loot table in loot.json points to it with "type": "object". Its kind must not be equippable.

{
  "id": "mod-smoke-vial", "kind": "active",
  "name": "ACTIVE_SMOKE_VIAL_NAME", "icon": "!",
  "description": "ACTIVE_SMOKE_VIAL_DESCRIPTION", "power": 0,
  "use": { "scope": "direction", "range": 5,
    "effects": [{ "action": "field",
      "params": { "field": "smoke", "radius": 1, "turns": 6 } }] }
}

Throwing it fills a radius of 1 with smoke for 6 turns. A reagent in resources.json can also declare a use with "scope": "item", to act on another item; write its effects explicitly.

Equipment glyphs#

Each piece of equipment is drawn with one character. glyphs in equipment.json sets it by kind, by base item, or by empty slot. A base glyph wins over its kind's.

{
  "kinds": { "weapon": "/", "armor": "[", "shield": "]", "helmet": "^", "boots": "⌟", "gloves": "(", "ring": "=", "amulet": "\"" },
  "bases": { "pilgrims-staff": "|", "crossbow": "}" },
  "slots": { "ring-left": "○", "ring-right": "○", "neck": "⌐" }
}
In game

Crossbows show as } and pilgrim's staves as |. Other weapons show as /. Empty ring and neck slots show their own signs.

Next#