11 KiB
Ability JSON Schema Documentation
This document explains the structure of ability definition files used in the system. It is intended for wiki contributors and tool developers alike.
Top-Level Structure
Every ability file follows this wrapper:
{
"abilities": {
"id": "namespace:category/name",
"displayName": "translation.key.for.name",
"description": "translation.key.for.description",
"math": [ ...nodes ]
}
}
| Field | Type | Required | Description |
|---|---|---|---|
id |
string | ✅ | Unique ID in namespace:category/name format |
displayName |
string | ✅ | i18n translation key for the display name |
description |
string | ✅ | i18n translation key for the tooltip/description |
math |
array | ✅ | Ordered list of effect nodes (see below) |
The id Format
IDs follow the pattern: namespace:category/name
| Part | Example | Notes |
|---|---|---|
namespace |
original |
The mod or source that owns this ability |
category |
fire |
The element, school, or grouping |
name |
fireball |
The specific ability |
Examples:
original:fire/fireballoriginal:ice/frost_boltmymod:arcane/void_lance
Translation Keys
displayName and description are not raw text — they are keys looked up in a translation file.
Convention: ability.<namespace>.<name>.<field>
| Field | Example Key |
|---|---|
displayName |
ability.original.fireball.name |
description |
ability.original.fireball.description |
Math Nodes
The math array is the heart of an ability. Each node is an object with at minimum:
{ "id": "unique_node_id", "type": "node_type", ...fields }
| Field | Type | Required | Description |
|---|---|---|---|
id |
string | ✅ | Unique name for this node within the ability |
type |
string | ✅ | Determines what this node does (see types below) |
Node Types
base_value
The foundational damage or healing number, before any modifiers.
{
"id": "base_damage",
"type": "base_value",
"amount": 50,
"scaling": { "stat": "spell_power", "multiplier": 1.5 }
}
| Field | Type | Required | Description |
|---|---|---|---|
amount |
number | ✅ | The flat base value |
scaling.stat |
string | ✅ | Which player stat to scale from |
scaling.multiplier |
number | ✅ | How much of that stat to add (stat × multiplier) |
Formula:
final = amount + (player[stat] × multiplier)
range
How far the ability can reach and how it travels.
{
"id": "cast_range",
"type": "range",
"min": 0,
"max": 30,
"unit": "meters",
"rangeType": "projectile"
}
| Field | Type | Required | Description |
|---|---|---|---|
min |
number | ✅ | Minimum range (use > 0 for dead zones) |
max |
number | ✅ | Maximum range (null = unlimited) |
unit |
string | ✅ | "meters" |
rangeType |
string | ✅ | projectile, hitscan, melee, aura |
area_of_effect
Defines the shape and spread of the ability's impact zone.
{
"id": "explosion",
"type": "area_of_effect",
"shape": "sphere",
"radius": 5,
"unit": "meters",
"falloff": "linear"
}
| Field | Type | Required | Description |
|---|---|---|---|
shape |
string | ✅ | sphere, cone, cylinder, line |
radius |
number | ✅ | Size of the AoE |
unit |
string | ✅ | "meters" |
falloff |
string | ✅ | none, linear, quadratic — how damage drops off at edges |
damage
Instant damage dealt. Supports multiple damage types in one node via sources.
{
"id": "instant_damage",
"type": "damage",
"sources": [
{
"damageType": "fire",
"base_value": 50,
"scaling": { "stat": "spell_power", "multiplier": 1.5 }
},
{
"damageType": "physical",
"base_value": 15,
"scaling": { "stat": "strength", "multiplier": 0.5 }
}
]
}
| Field | Type | Required | Description |
|---|---|---|---|
sources |
array | ✅ | One entry per damage type |
sources[].damageType |
string | ✅ | e.g. fire, physical, lightning, poison |
sources[].base_value |
number | ✅ | Flat damage for this type |
sources[].scaling |
object | ✅ | Same stat / multiplier structure as base_value |
Note: Multiple sources are applied independently — each scales off its own stat.
damage_over_time
Repeating damage applied in ticks after the initial hit.
{
"id": "burn_dot",
"type": "damage_over_time",
"damageType": "fire",
"damage_per_tick": 10,
"tick_interval_seconds": 1,
"duration_seconds": 5,
"scaling": { "stat": "spell_power", "multiplier": 0.3 },
"stacks": false
}
| Field | Type | Required | Description |
|---|---|---|---|
damageType |
string | ✅ | Damage type per tick |
damage_per_tick |
number | ✅ | Flat damage each tick |
tick_interval_seconds |
number | ✅ | Seconds between ticks |
duration_seconds |
number | ✅ | Total duration |
scaling |
object | ✅ | Same stat / multiplier structure |
stacks |
boolean | ✅ | true = re-applying adds a new stack; false = resets timer |
Total ticks:
duration_seconds / tick_interval_seconds
condition
A status effect or triggered reaction applied to the target.
{
"id": "ignite_debuff",
"type": "condition",
"chance": 0.75,
"duration_seconds": 5,
"effect": "reduce_fire_resistance",
"magnitude": -20
}
{
"id": "explosion_knockback",
"type": "condition",
"chance": 1.0,
"effect": "knockback",
"force": 8,
"direction": "away_from_origin"
}
| Field | Type | Required | Description |
|---|---|---|---|
chance |
number | ✅ | Probability 0.0–1.0 (1.0 = always) |
effect |
string | ✅ | The condition to apply — mapped by the engine |
duration_seconds |
number | ❌ | How long the condition lasts (omit for instant effects) |
magnitude |
number | ❌ | Numeric modifier for stat-changing effects |
force |
number | ❌ | For displacement effects like knockback |
direction |
string | ❌ | away_from_origin, toward_origin, up |
Multiple
conditionnodes are rolled independently per hit.
meta
Gameplay configuration — cooldowns, costs, and tags.
{
"id": "fireball_meta",
"type": "meta",
"cooldown_seconds": 12,
"mana_cost": 80,
"cast_time_seconds": 1.5,
"tags": ["fire", "aoe", "projectile", "dot"]
}
| Field | Type | Required | Description |
|---|---|---|---|
cooldown_seconds |
number | ✅ | Recharge time after use |
mana_cost |
number | ✅ | Resource cost to cast |
cast_time_seconds |
number | ✅ | Time before the ability fires (0 = instant) |
tags |
string[] | ✅ | Used for filtering, synergies, and resistances |
Complete Example — Fireball
{
"abilities": {
"id": "original:fire/fireball",
"displayName": "ability.original.fireball.name",
"description": "ability.original.fireball.description",
"math": [
{
"id": "base_damage",
"type": "base_value",
"amount": 50,
"scaling": { "stat": "spell_power", "multiplier": 1.5 }
},
{
"id": "cast_range",
"type": "range",
"min": 0,
"max": 30,
"unit": "meters",
"rangeType": "projectile"
},
{
"id": "explosion",
"type": "area_of_effect",
"shape": "sphere",
"radius": 5,
"unit": "meters",
"falloff": "linear"
},
{
"id": "instant_damage",
"type": "damage",
"sources": [
{
"damageType": "fire",
"base_value": 50,
"scaling": { "stat": "spell_power", "multiplier": 1.5 }
},
{
"damageType": "physical",
"base_value": 15,
"scaling": { "stat": "strength", "multiplier": 0.5 }
}
]
},
{
"id": "burn_dot",
"type": "damage_over_time",
"damageType": "fire",
"damage_per_tick": 10,
"tick_interval_seconds": 1,
"duration_seconds": 5,
"scaling": { "stat": "spell_power", "multiplier": 0.3 },
"stacks": false
},
{
"id": "ignite_debuff",
"type": "condition",
"chance": 0.75,
"duration_seconds": 5,
"effect": "reduce_fire_resistance",
"magnitude": -20
},
{
"id": "explosion_knockback",
"type": "condition",
"chance": 1.0,
"effect": "knockback",
"force": 8,
"direction": "away_from_origin"
},
{
"id": "fireball_meta",
"type": "meta",
"cooldown_seconds": 12,
"mana_cost": 80,
"cast_time_seconds": 1.5,
"tags": ["fire", "aoe", "projectile", "dot"]
}
]
}
}
Quick Reference Card
| Node Type | Purpose | Key Fields |
|---|---|---|
base_value |
Core damage number + stat scaling | amount, scaling |
range |
How far & how the ability travels | min, max, rangeType |
area_of_effect |
Shape & spread of impact zone | shape, radius, falloff |
damage |
Instant hit damage (multi-type ok) | sources[] |
damage_over_time |
Tick damage over time | damage_per_tick, tick_interval_seconds, duration_seconds, stacks |
condition |
Status effects & reactions | chance, effect, duration_seconds |
meta |
Cooldown, cost, cast time, tags | cooldown_seconds, mana_cost, cast_time_seconds, tags |
Rules & Conventions
- Every ability must have at least one
metanode. idvalues must be unique within the same ability file — use descriptive names.displayNameanddescriptionmust be translation keys, never raw text.- The
matharray is processed top to bottom — order matters for dependent effects. damagenodes should come after anyarea_of_effectnodes that modify their area.- A
damage_over_timenode is separate fromdamage— both can coexist freely. - Multiple
conditionnodes are each rolled independently. tagson themetanode are used by the engine for resistance checks, talent synergies, and UI filtering.