Rules In Eldiron
This page explains how the official rules are applied inside Eldiron.
For the player-facing rulebook, see Official Rules. This page is about storage, embedding, project selection, Creator behavior, runtime resolution, project-level rule overrides, and rule testing.
Source Of Truth
The official ruleset lives in the eldiron-ruleset crate:
crates/ruleset/rulesets/
manifest.toml
eldiron/
v1/
ruleset.toml
identity.toml
attributes.toml
progression.toml
combat.toml
economy.toml
messages.toml
locales.toml
equipment.toml
fx.toml
icons.toml
invocations.toml
conditions.toml
actions.toml
abilities_spells.toml
races_classes.toml
README.md
assets/
humanoid.eldiron_avatar
orc.eldiron_avatar
skeleton.eldiron_avatar
This location is intentional. The ruleset is not owned by Creator only, and it must be publishable with the crate that exposes the official ruleset API. It is available to:
- Creator
- graphical clients
- terminal clients
- shared runtime code through
eldiron-rulesetandeldiron-shared - calculators
- automatic arena tools
- tests
- documentation generators
The current built-in ruleset is eldiron.official version 1.0.0.
Compile-Time Embedding
Official rulesets are embedded at compile time by the eldiron-ruleset crate.
The ruleset crate includes all official v1 TOML parts with
include_str!, joins them into one effective official TOML source, and also
embeds the bundled humanoid, orc, and skeleton avatar assets.
This lets every binary built from the repository access the same official ruleset through a package-safe crate API without each app carrying its own private copy.
Project Selection
A project selects its ruleset in Game / Settings with the top-level
[ruleset] section:
[ruleset]
id = "eldiron.official"
version = "1.0.0"
schema_version = "1"
source = "official"
update_policy = "compatible"
The section is top level because other main game settings are top level too.
Supported intent:
source = "official"uses a bundled ruleset selected byidandversion- Game / Rules can override that official ruleset for this project
update_policydescribes how future compatible updates should be handled
Older projects that do not have [ruleset] are migrated by adding this default
section.
Game / Rules
For official-rules projects, Game / Rules is the project-level override layer. It is empty by default because new projects use the bundled Eldiron Official Ruleset unchanged.
The default template explains this:
# Game / Rules is the project-level override layer for the official ruleset
# selected in Game / Settings.
During the v1 cleanup, normal gameplay definitions should move out of character and item attributes and into the official ruleset or this project-level Game / Rules override. Character and item attributes should not redefine cooldowns, spell behavior, class permissions, intent distance, or combat math.
Ruleset timing values use seconds. Script scheduling commands such as
notify_in, block_events, patrol waits, and random-walk sleeps still use
in-game minutes because they operate on the world clock. This keeps ruleset
combat tuning separate from authoring-time world schedules.
The effective ruleset is resolved like this:
- Read
[ruleset]from Game / Settings. - Load the matching bundled official ruleset.
- Merge Game / Rules TOML on top.
- Use the merged result for runtime and tools.
Ruleset localizations are resolved the same way:
- Load the bundled English locale defaults for the selected official ruleset.
- Merge Game / Locales TOML on top.
- Use project locale entries as overrides, not as a required copy of every ruleset message.
Configuration And Overrides
Official ruleset projects are configured in layers.
Use Game / Settings to select which bundled ruleset the project follows. Use Game / Rules to override ruleset TOML for this project. The override should contain only the tables and keys that are intentionally different from the bundled official ruleset.
Use Game / Locales the same way for text. Project locale entries replace matching bundled ruleset locale keys, while missing keys continue to come from the official locale defaults.
Project assets can also override bundled ruleset assets when they use the same
lookup name. Ruleset avatars are loaded first, then project avatars are inserted
afterwards by avatar name. This means a project avatar named humanoid
overrides the bundled official humanoid avatar automatically.
This is important for artist-edited avatar atlases. If you export the official
humanoid avatar as a PNG atlas, edit it externally, and import it back into a
project avatar named humanoid, all characters that use the default ruleset
avatar will use the project version. A project avatar named Human does not
replace the default humanoid avatar by name; it is used only by characters
that explicitly set avatar = "Human" or the matching avatar_id.
Explicit character and item presentation still wins over default ruleset
presentation. A character with avatar, avatar_id, tile_id, or source
does not use the fallback ruleset avatar. Setting an empty avatar = "" or
tile_id = "" is a deliberate way to prevent inherited default visuals.
No Backwards Compatibility Requirement
The official ruleset replaces the old ad hoc rules model.
Old projects are migrated toward the new shape by:
- adding the default
[ruleset]section when missing - replacing old project rules with the empty Game / Rules override template
This is allowed because the goal is to create one coherent default ruleset instead of preserving every old formula shape forever. The official v1 rules should prefer explicit tables and dice-like values.
Character Defaults
Identity defaults are optional ruleset references:
[identity.defaults]
race = "Human"
class = "Warrior"
When present, they must name declared race/class entries. When absent, Eldiron does not invent an official race or class: a classless sandbox stays classless, including its start UI and action-bar slot resolution. An explicit character race or class still wins over the default.
When a character starts, ruleset defaults are applied in this order:
- global attribute defaults
- default race and class when the character has none
- race defaults
- class defaults
- class starting loadout, unless the character defines explicit startup items
Character attributes identify the concrete character and store runtime state. They should not redefine rules that already live in the official ruleset.
For example, a minimal character can set:
[attributes]
race = "Human"
class = "Warrior"
The runtime can then apply Human and Warrior defaults from the effective ruleset.
For settlement NPCs, keep combat identity and economic role separate:
[attributes]
race = "Human"
class = "Citizen"
profession = "Blacksmith"
Citizen gives the NPC a civilian baseline. profession is available for shop,
service, crafting, training, and dialogue rules without turning every merchant
or smith into a combat class.
Professions are role labels, not hard crafting caps. Recipe access is gated by
ruleset skills such as fletching, herbalism, alchemy, ritualism, or
restoration. A character can carry skill points with attributes like
skill_fletching = 25, while simple recipes can require 0 skill and be
available immediately. Recipes can also require known spells, such as
moonwater requiring minor_heal.
If a character does not define start_equipped_items,
startup_equipped_items, or add_equip_items, the class loadout supplies
equipped weapons, armor, and clothing. If a character does not define
start_items, startup_items, or add_items, the class loadout supplies its
starting inventory.
Explicit character startup item attributes always override the class loadout.
start_active_items can name any item in either explicit startup list when the
game needs it to spawn active. The item still receives its normal ruleset
active event, so state-specific visuals, light, durability, and other behavior
remain defined by the item rather than the character.
Rulesets assign engine-facing meaning through optional semantic attribute roles. The official mapping is:
[attributes.roles]
health = "HP"
max_health = "MAX_HP"
level = "LEVEL"
experience = "EXP"
weapon_damage = "DMG"
armor = "ARMOR"
A different ruleset can use names such as VITAL, RANK, HARM, and WARD;
damage, healing, respawn, XP, equipment summaries, graphical UI placeholders,
and terminal stats follow the mapping. The role values must reference
attributes declared by the same ruleset. A classless sandbox may omit
progression roles, in which case Eldiron does not invent LEVEL or EXP.
Class resource growth uses a generic list rather than HP/MP-specific fields:
[classes.Cleric.progression.level]
resource_gains = [
{ attribute = "HP", maximum_attribute = "MAX_HP", per_level = 5 },
{ attribute = "MP", maximum_attribute = "MAX_MP", per_level = 3 },
]
primary_attribute_gain = 1
Custom resources use the same shape. The current and maximum attributes grow together while an explicitly authored current value—such as an injured character's HP—is preserved.
Action costs use the same generic attribute ids:
[actions.focus_bolt]
kind = "spell"
cost = { FOCUS = 4 }
result = { damage = "spells.focus_bolt.damage" }
All typed resource costs are checked before the cast and committed by attribute
name. MP has no privileged runtime branch.
Class unlock tables are the sole gameplay owner of when abilities and spells become known:
[classes.Warrior.unlocks.level_1]
abilities = ["basic_attack", "guard"]
[classes.Warrior.unlocks.level_2]
abilities = ["power_strike"]
[classes.Warrior.unlocks.level_10]
abilities = ["executioner_strike"]
Do not duplicate these lists on the class or in starting_loadout; the latter
owns items only. The runtime materializes every unlock at or below the
character's level, the server uses the same table for authorization, and the
action UI uses it to explain future unlock levels. Validators reject unlocks
outside progression.level.max_level, unknown references, unlocked definitions
without an action, and broken rules.* action-bar commands.
Level thresholds may be explicit:
[progression.level]
max_level = 10
[progression.xp_table]
level_2 = 100
level_3 = 250
or formula-driven:
[progression.level]
max_level = 30
xp_for_level = "level * level * 100"
max_level is authoritative for both forms. Runtime XP grants cannot advance
past it, XP-table rows above it are validation errors, and a classless game may
omit classes and the entire progression module without warnings.
Intent Rules
Common intent policy belongs in the effective ruleset.
An action can bind an engine input intent. The binding is case-insensitive and must be unique:
[actions.strike]
kind = "attack"
intent = "attack"
target = "hostile_or_neutral_entity"
range = "weapon"
cooldown = 1.0
result = { damage = "weapon" }
The action id may be anything. The action bound to attack supplies ordinary
and follow-attack requirements, damage, range, ammunition, and cooldown, so a
custom ruleset does not need an action named basic_attack.
Action target kinds describe who or what the action can affect:
hostile_entity: hostile targets onlyhostile_or_neutral_entity: hostile and neutral targets, but not friendly targetsfriendly_entity: friendly targets onlyfriendly_or_self: friendly targets or the acting characterany_entity: any character targetground_item: a nearby item on the groundresource_node: a ruleset resource item such as a herb or wood nodeself: the acting character
The runtime resolves the target disposition from race relations and reputation.
Reputation defaults to 0, which means normal: keep the base race relation.
Rules should use structured keys that tools can validate.
Derived Stats
Rulesets may calculate an effective attribute without storing the calculated result on every character:
[derived_stats.POWER]
formula = "base + floor(max(0, INT - 10) / 4) + floor(level / 5)"
minimum = 0
[derived_stats.MAX_MP]
formula = "base + WIS"
minimum = 0
base is the recipient's saved value for the derived stat. level reads the
ruleset-configured level attribute. Other identifiers resolve through the same
effective-attribute path, so formulas can depend on ordinary attributes or
other derived stats. Dependencies are cycle-validated; the runtime also has a
cycle guard.
Formula syntax supports +, -, *, /, comparisons, &&, ||,
parentheses, and min, max, clamp, abs, floor, ceil, and round.
Optional minimum and maximum fields clamp the formula result.
The server uses effective values for combat formulas, numeric action
requirements, maximum_attribute action clamps, healing, resource
regeneration caps, and respawn health. The action UI evaluates the same
formula. Conditions modify dependencies before they enter a formula and then
modify the resulting derived stat. Nothing writes the calculated result back
to the saved base attribute.
Conditions
Conditions are reusable timed or persistent state. Actions apply or remove them, while the condition definition owns duration, stacking, tags, trait immunities, numeric attribute modifiers, periodic effects, and visual phases:
[conditions.guarded]
name = "Guarded"
duration = 2.0
stacking = "refresh"
max_stacks = 1
tags = ["stance", "beneficial"]
immune_traits = []
modifiers = [{ attribute = "ARMOR", add = 2 }]
[conditions.weakened]
duration = 4.0
stacking = "stack"
max_stacks = 3
modifiers = [
{ attribute = "POWER", add = -0.5, multiply = 0.9, minimum = 1 },
]
[conditions.guarded.fx.apply]
preset = "hit_burst"
[conditions.guarded.fx.active]
preset = "holy_glow"
[conditions.poisoned.periodic]
interval = 1.0
initial_delay = 1.0
effects = [
{ damage = 2, damage_kind = "poison" },
{ resource = "STAMINA", add = -1, minimum = 0 },
]
[conditions.poisoned.fx.tick]
preset = "poison_burst"
[actions.guard]
kind = "stance"
target = "self"
cooldown = 3.0
result = { apply_condition = "guarded" }
Valid stacking policies are replace, refresh, stack, and ignore.
max_stacks may exceed one only for stack. A zero duration persists until
removed. The runtime exposes conditions, condition_<id>_remaining, and
condition_<id>_stacks on the entity for UI and scripts. Active periodic
conditions also expose condition_<id>_tick_remaining; applying-source
identity is stored as condition_<id>_source. Periodic effects may deal typed
damage, heal, or add to a numeric attribute/resource with optional minimum and
maximum bounds. Their values scale with the active stack count.
Static modifiers accept add, multiply, minimum, and maximum; at least
one operation is required. Evaluation is independent of TOML table order:
- sum every
add × stacks - multiply by every
multiply ^ stacks - apply the strongest minimum and strongest maximum
Combat formulas and numeric action requirements read this effective value without overwriting the entity's saved base attribute; the action UI uses the same calculation. A multiplier must be non-negative, and a modifier's minimum cannot exceed its maximum. If separately authored active bounds conflict, the strongest maximum is applied last.
Condition FX stages are apply, active, tick, and remove. The active
stage is a replicated persistent particle emitter that follows the affected
entity until the condition ends. Each other stage is a one-shot ruleset FX
preset. An explicit [conditions.<id>.fx.<stage>] wins. If it is absent, the
runtime may use [fx.condition_fallbacks].<stage>; if that mapping is also
absent, no particle is required.
Scripts may observe condition_applied, condition_tick, and
condition_removed. Their payload contains applying source id in x, stack
count in y, remaining seconds in z, and the condition id as the string.
These mirrored attributes are the condition save contract. Because they live
on the serialized entity, active ids, stacks, remaining duration, periodic
phase, and stable source creator_id survive a map save/load. Timers pause
while the region is unloaded. On restoration, stale or expired definitions are
dropped, the source is resolved to its new runtime id (or falls back to the
affected entity), and only the persistent active FX is rebuilt. Transient
ruleset FX items are never restored, and restoration does not replay lifecycle
events or one-shot apply particles.
Optional Invocation Schemes
Ruleset actions can have optional token-sequence bindings. The official example defines the available words separately from the action:
[invocation_schemes.words_of_power]
kind = "token_sequence"
tokens = ["LO", "VI", "FUL", "YA", "IR", "SAR"]
separator = " "
max_tokens = 4
case_sensitive = false
[actions.minor_heal]
invocations = [
{ scheme = "words_of_power", sequence = ["LO", "VI"] },
]
A screen can assemble tokens in any style and submit:
command = "intent.invoke:words_of_power:{UI.spell.runes}"
The server resolves the phrase to the bound action before normal targeting and execution. This keeps word/rune interfaces, icon action bars, hotkeys, and text commands interchangeable. Invocation tokens do not require icons.
Item Templates
Ruleset items are gameplay definitions. Creator still needs real project item templates so users can drag items onto the map.
Creator therefore syncs ruleset-backed item templates from ruleset definitions when a project is opened or created.
For example, a ruleset entry like:
[items.weapons.training_sword]
name = "Training Sword"
description = "A blunt wooden practice sword used for early drills and safe sparring."
category = "sword"
slot = "main_hand"
rarity = "common"
icon = "training_sword"
visual_template = "sword_diagonal"
becomes a normal project item template tagged with:
[attributes]
ruleset_path = "items.weapons.training_sword"
ruleset_kind = "weapon"
ruleset_id = "training_sword"
on_look = "A blunt wooden practice sword used for early drills and safe sparring."
Creator creates missing ruleset-backed items and refreshes existing
ruleset-backed items whose ruleset_path still points to the official item.
Custom project items remain separate project assets.
Ruleset icons live in [icons] and are bundled as neutral PNG masks. Item
templates can set icon = "training_sword" as a generic fallback. Item display
still prefers explicit tiles, avatar channels, and visual_template pixel masks
when present, so hand-shaped pixel item icons remain the primary look.
Icons are shared semantic assets rather than one file per definition: the
official Guard action, for example, deliberately references the existing
round_shield icon.
Actions and items may share any semantic icon id, so new content does not
require one-off artwork. Action icons resolve an explicit ui.icon, then an
icon matching the required ability, required spell, or action id, then
[ui.action_icon_fallbacks] by healing, condition, action kind, and
default. Item icons resolve an explicit icon/icon_template, then
[ui.item_icon_fallbacks] by ruleset_kind and default. Ruleset-backed
project item templates receive the resolved item fallback during sync.
Action particle stages work the same way. Explicit
[actions.<id>.fx.<stage>] data wins; otherwise
[fx.action_fallbacks.<semantic-role>].<stage> may supply a shared preset.
Healing and condition roles precede the action kind. Attack, spell, stance, and
other mappings are authored by the ruleset rather than hardcoded by official
action id. These fallback tables are optional, and all referenced icon and FX
preset ids are validated.
Ruleset-backed item templates can also carry item script source, authoring text,
tile ids, and lights. The ruleset can bundle the referenced tiles too, including
animated tile frames. This is used for reusable interactive objects such as
items.tools.torch: the ruleset creates a normal project item template whose
use intent toggles active, swaps between the bundled unlit tile and the
bundled four-frame lit animation, enables or disables the point light, and
presents different look/use text for the on and off states. The same item also
uses ruleset durability: while active, its condition drains in game minutes,
and the default official torch destroys itself at 0% condition.
Ruleset item ids are stable. Startup loadouts can reference training_sword or
padded_armor even when the visible item name is Training Sword or
Padded Armor.
Equipment Policy
Slots, category conflicts, and class permissions are authored in the ruleset rather than hardcoded by the engine:
[equipment]
weapon_slots = ["main_hand", "off_hand"]
armor_slots = ["head", "torso", "legs", "hands", "feet", "shield"]
[equipment.avatar_anchors]
main_hand = "main_hand"
off_hand = "off_hand"
shield = "off_hand"
[equipment.weapon_categories.spear]
handed = "two_handed"
[equipment.armor_categories.shield]
occupies_slots = ["off_hand"]
[classes.Warrior]
allowed_weapons = ["sword", "axe", "mace", "spear", "bow"]
allowed_armor = ["cloth", "leather", "chain", "shield"]
The item's declared slot is always occupied. A two-handed weapon additionally
occupies all weapon_slots, and occupies_slots adds any category-specific
conflicts. In the example, a shield is stored in shield but consumes
off_hand, so it cannot coexist with a bow or spear.
Slot arrays are ordered and duplicate IDs are invalid. For weapon damage,
range, cooldown, script source context, and UI/terminal equipment totals,
Eldiron checks equipped weapon slots in the authored order. The first occupied
weapon slot is the primary attack source. There is no second slot list in
Game / Settings and the engine does not guess names such as main_hand;
custom names work directly.
avatar_anchors maps any declared slot name onto the avatar renderer's
main-hand or off-hand frame anchor. It is optional; omit it for text-only games
or equipment that should not be drawn on the avatar.
Class permission arrays are independently optional:
- missing
allowed_weaponsorallowed_armormeans unrestricted for that family - an explicit empty array means the class may equip none from that family
- clothing follows
allowed_armor - a classless character remains unrestricted by class, but still follows slots and handedness
The validator checks starting loadouts against this same policy. At runtime, startup, drag/drop, command, and script equip operations all use one cached policy and reject changes without losing or moving the item.
Palette Ownership
The official ruleset owns the Ruleset Palette.
On load and ruleset sync, Eldiron resolves the effective ruleset [palette]
into the project's Ruleset Palette. Rules-owned visuals such as official item
icons, avatar defaults, UI color channels, and generated ruleset assets can rely
on those indices staying stable.
The editable Art Palette is separate. It is used for artist-authored tiles, pixel drawing, tile graphs, palette-index geometry sources, and 3D Paint.
For ruleset-driven projects:
- Ruleset Palette changes should be made by overriding
[palette]in Game / Rules. - Art Palette changes are made with the Palette Tool and do not alter rules-owned indices.
- palette clear/import actions affect the Art Palette only.
- Art Palette material preset/finish metadata remains editable project render metadata.
Visual Defaults
The official ruleset can bundle default visual assets.
The global fallback avatar reference is:
[visuals.defaults]
avatar = "humanoid"
The bundled asset lives in the ruleset directory:
crates/ruleset/rulesets/eldiron/v1/assets/humanoid.eldiron_avatar
crates/ruleset/rulesets/eldiron/v1/assets/orc.eldiron_avatar
crates/ruleset/rulesets/eldiron/v1/assets/skeleton.eldiron_avatar
Runtime asset loading makes this available to clients. Character visuals can
still provide concrete presentation with values such as tile_id or avatar.
An explicit project visual wins over the ruleset default, and a project avatar
named humanoid, orc, or skeleton replaces the matching bundled default
avatar for the project. The bundled Skeleton file is a distinct copy of the
humanoid asset intended as the import target for a dedicated Skeleton atlas.
The official ruleset also demonstrates race traits as gameplay data rather than
engine branches. Skeleton materializes traits = ["undead", "skeletal"];
the Cleric's turn_undead action uses a generic target_attributes membership
predicate and applies the ordinary particle-backed turned condition. Custom
races, traits, predicates, and conditions use the same path.
Ruleset items can also define avatar_channels:
[items.clothing.linen_shirt]
color = 2
worth = 5
avatar_channels = ["torso", "arms"]
When no explicit item icon or tile source is provided, Eldiron uses the default avatar's idle front frame, extracts the requested channels, recolors them from the ruleset palette, and uses that shape for inventory, equipped slot, and ground item previews.
Runtime Resolution
Runtime systems should use the effective ruleset, not scattered character or item rule attributes.
That means clients and shared runtime helpers resolve rules by combining:
- the selected bundled ruleset
- the project-level Game / Rules override
- concrete character or item identity/state such as race, class, level, and equipment
The practical result is that Creator, graphical clients, terminal clients, and shared server logic all answer the same rules questions.
Testing Rules
Rules can be tested in the terminal client:
eldiron-client-terminal rules check
eldiron-client-terminal rules check test_projects/Hideout2D.eldiron
eldiron-client-terminal rules summary
eldiron-client-terminal rules character Cleric race=Human level=2
eldiron-client-terminal rules character Ranger race=Human level=1
eldiron-client-terminal rules item training_sword STR=12
eldiron-client-terminal rules item hunting_bow DEX=12
eldiron-client-terminal rules item linen_shirt
eldiron-client-terminal rules class Warrior
eldiron-client-terminal rules recipe wooden_arrows
eldiron-client-terminal rules recipe hunting_bow
eldiron-client-terminal rules xp 5
eldiron-client-terminal rules weapon training_sword STR=12
eldiron-client-terminal rules spell fire_spark INT=12
eldiron-client-terminal rules roll items.weapons.training_sword.damage STR=12
The same style of command is also available in Creator's Game / Console:
rules overview
rules validate
rules list
rules list classes
rules show items.weapons.training_sword
rules class Warrior
rules show recipes.wooden_arrows
rules show recipes.hunting_bow
rules xp 5
rules weapon training_sword STR=12
rules spell fire_spark INT=12
rules roll items.weapons.training_sword.damage STR=12
Use the inspector commands to browse the effective ruleset:
rules overview: show active ruleset metadata and section countsrules validate: check references, rolls, XP tables, visuals, items, spells, and classesrules list: list races, classes, professions, skills, recipes, weapons, armor, spells, abilities, actions, conditions, and invocation schemesrules list <section>: list one sectionrules show <path>: show the TOML at a ruleset path
Use the calculator commands to answer balancing questions without needing to run a full gameplay scenario.
In play, official action distances are resolved before per-character
[intent_distance] values. The same attack icon can therefore use melee
range for swords and maces, or bow range for Rangers. Directional 2D intents
scan the chosen lane up to that range, so attack plus a direction can select a
hostile target beyond the adjacent tile when the equipped weapon allows it.
Weapons can also declare ammunition. For example, hunting_bow requires
wooden_arrows and ammunition_quantity = 1; a successful weapon attack
consumes that quantity from matching inventory stacks before damage is queued.
Stackable inventory items use quantity for the current count and max_stack
for slot capacity. The same stack-counting path is used by action consumes
entries for reagents, materials, and future crafting inputs. For example,
minor_heal consumes 1 blessed_herb and 1 moonwater only after target,
range, MP, and effect checks pass.
Regenerating resources use top-level resource_regen rules. For example,
[resource_regen.MP] restores mana over real-time seconds, carries fractional
progress between ticks, and clamps the result to MAX_MP. This keeps MP
restoration in the ruleset instead of in individual scripts or screen widgets.
Resource nodes are separate from inventory materials. For example,
wild_herb_node is a placed world item with static = true, resource_id = "wild_herb_node", respawn = 300, and amount = 2. Gathering it with
gather_herbs adds wild_herb x2 to the actor's inventory, hides the node, and
lets it become visible again after its respawn timer. It also sends a localized
success message such as You gather Wild Herb x2. green_wood_node works the
same way for gather_wood, producing green_wood x3, while bird_nest_node
uses gather_feathers to produce feather x2. The same representation defines
Moonleaf Patches, Sunstone Outcrops, Old Graves, and Resinous Stumps for ritual
materials. The text command path can use these actions too:
gather herbs
gather wood
gather feathers
gather moonleaf
mine sun shards
sift grave dust
tap ember resin
craft blessed herb
craft wooden arrows
craft hunting bow
craft moonwater
craft consecrated oil
craft warding salt
craft ember beads
When no target is named, the text command chooses the nearest visible resource node for that action and leaves range validation to the rules action.
Containers
Item containers are normal ruleset item templates with container = true and
container_slots. The first official container is small_bag, a takeable
six-slot pouch.
Container UI is ruleset-driven, not screen-driven. Items can select a
container_template, and the runtime opens a floating draggable panel. The
panel can be closed with Escape or its close button. Inventory items can be
dragged into the panel, and items inside the panel can be dragged back to
inventory slots, equipment slots, or the map. Clicking an item inside an open
container transfers it to the first free player inventory slot. It is drawn
procedurally when no tile skin is supplied:
[ui.container_templates.bag_small]
mode = "procedural"
columns = 3
rows = 2
slot_size = 32
gap = 4
padding = 8
title = true
[items.containers.small_bag]
container_template = "bag_small"
Template tile fields can be supplied under
[ui.container_templates.<id>.tiles] for top_left, top, top_right,
left, center, right, bottom_left, bottom, bottom_right, and slot.
If those fields are absent, the procedural renderer is used.
The current text command path can move top-level inventory items into and out of an inventory or visible world container, and can open a container floater:
open small bag
put wild herb in bag
take wild herb from bag
Stackable items merge inside containers. When a dead character script calls
drop_items(""), the official rules create a lootable corpse container instead
of placing every carried item directly on the map. The corpse uses the normal
container UI and can be opened with open <name> or by clicking it. Once the
corpse is empty, the tombstone disappears when despawn_when_empty = true in
[loot.corpse]. Non-empty corpses use despawn_seconds. If the corpse belongs
to a respawning NPC, the timer is shortened by
despawn_before_respawn_seconds, so the body disappears shortly before the NPC
returns.
NPC respawn is also rules-driven. [respawn.npc] defaults to enabled, restores
NPC health to full, restores startup loadout and behavior state, and removes
the NPC corpse on respawn. Player characters are excluded from this automatic
path; their death and resurrection flow stays in the player script. For one
NPC, use respawn_seconds = 120 to change the delay or respawn = false to
keep it dead.
Economy
The official economy lives in economy.toml. Runtime wallets store one integer
base amount. In v1 the base is copper:
[economy]
base = "copper"
[economy.starting_wealth]
player = 50
[economy.currencies.copper]
symbol = "c"
value = 1
[economy.currencies.silver]
symbol = "s"
value = 10
[economy.currencies.gold]
symbol = "g"
value = 100
Item worth, shop prices, rewards, and wealth overrides are measured in base
units. The UI can format the same balance compactly, so 125 displays as
1g 2s 5c. New player characters start with 50 base units, displayed as
5s, unless their character attributes define an explicit wealth. Use
{PLAYER.MONEY} for formatted display and {PLAYER.FUNDS} when raw base units
are needed for tests or logic.
Currency items are ordinary ruleset-backed item templates marked
monetary = true. Taking them adds their base value to the wallet instead of
placing the item in inventory.
To make a money loot item with a specific value, set the currency and amount on the item instance or template:
[attributes]
monetary = true
currency = "silver"
amount = 5
worth = 50
Recipes
Recipes live in recipes.toml and use the same source of truth as items,
actions, skills, professions, and spells. The official set includes immediate
crafts and a multi-stage ritual economy:
wooden_arrows: consumesgreen_wood x1andfeather x2, produceswooden_arrows x10blessed_herb: requiresminor_heal, consumeswild_herb x1, producesblessed_herb x1hunting_bow: recommendsskill_fletching = 25, consumesgreen_wood x3, produceshunting_bow x1moonwater: distillsmoonleaf x2intomoonwater x2consecrated_oil: combinesblessed_herb x1andsun_shard x1warding_salt: purifiesgrave_dust x2withsun_shard x1ember_beads: shapesember_resin x2intoember_bead x3ritual_censer: invests three material families in reusable spell equipmentsunward_charm: turns advanced ritual materials into reusable resistance
Recipe execution consumes input stack quantities and merges output stack quantities into existing inventory slots when possible. This is the same economy path that later shops, gathering nodes, crafting stations, and profession services can use.
The text command path can craft known recipes by name:
craft wooden arrows
craft hunting bow
craft blessed herb
craft moonwater
craft consecrated oil
craft warding salt
craft ember beads
craft ritual censer
craft sunward charm
Recipes can also be exposed through rules actions such as
rules.craft_blessed_herb, rules.distill_moonwater,
rules.mix_warding_salt, and rules.craft_ritual_censer. This lets screen
command slots trigger the same recipe path as text commands and scripts while
keeping recipes as the source of truth for materials, spell gates, skill
targets, and outputs.
Recipes can still use required_skill for hard gates, but ordinary crafting is
better modeled through output quality. recommended_skill, difficulty, and a
supporting attribute such as DEX or WIS set crafted item quality from
1..100; crafted items start at condition = 100. Weapon damage scales by item
quality and condition, so a new Ranger can craft immediately but starts with
rougher gear.
When [crafting.skill_gain] is enabled, a successful recipe also advances its
skill. The official configuration grants one point per success, plus one while
below the recipe's recommendation, and stops awarding points twenty above that
recommendation. The skill definition's max remains the absolute cap. This
turns repeated low-tier preparations into a real path toward gated equipment;
failed attempts never grant skill.
Future Versioning
The project stores which ruleset version it expects.
This allows future games to request a specific ruleset:
[ruleset]
id = "eldiron.official"
version = "3.0.0"
source = "official"
Future versions can add or change rules while older projects keep the version they selected. Bugfixes, localization improvements, and compatible additions can still be shipped through bundled ruleset updates according to the selected update policy.