Races, classes, skills and feats are all data. Two front doors, one schema: YAML for server owners at
config/legendquest/,
JSON for datapacks and mods at
data/<pack>/legendquest/.
The restart rule, before anything else. Races, classes,
skills and feats are frozen registries - like vanilla enchantments, they load once when
the world starts. /reload refreshes item tags and
messages.yml only, so content changes need a
server restart. Ops get a chat notice saying exactly this when someone reloads. The
classic symptom of forgetting: your edit "did nothing".
A YAML file with a built-in entry's name overrides it - the
whole file replaces the entry, so copy the original and edit rather than writing a fragment. YAML
registers as legendquest:<name>; JSON registers as
<pack>:<name>.
All times everywhere are milliseconds (1000 = 1s), as in every
original LegendQuest doc. cooldown: 6 is six thousandths of a second, and the
panel will cheerfully show you a skill with no cooldown.
Races
name: Dwarf
plural: Dwarves
chat_tag: "[Dw]"
description: "Stone remembers them."
size: 1.4
base_health: 24
base_mana: 6
mana_per_second: 0.6
base_speed: 0.18
statmods:
con: 2
chr: -1
groups:
- Humanoid
- Stout
- Dwarven
skill_points: 10
skill_points_per_level: 0.2
allow_enchanting: false
disallowed_weapons: ["#legendquest:mage_weapons"]
attributes:
minecraft:armor_toughness: { base: 1.0, per_level: 0.05 }
smith_refund: 0.2
skills:
legendquest:stone_skin:
level: 8
levels:
10: { hp: 2 }
25: { con: 1 }
| Field | Default | Meaning |
|---|---|---|
| name | (required) | Display name. |
| plural | (none) | "Dwarves", for text that needs it. |
| chat_tag | (none) | Short tag for chat prefixes. |
| description | (none) | One-liner for pickers and tooltips. |
| long_description | (none) | The handbook page's second paragraph. |
| size | 1.8 | Visual scale in blocks of height - 1.8 is a vanilla player. Hobbits are 1.1. |
| base_health | 20.0 | Max health before class and level bonuses. |
| base_mana | 10.0 | Mana pool before class bonuses. |
| mana_per_second | 1.0 | Regen. |
| base_speed | 0.2 | Old-scale walking speed: 0.2 is normal, 0.4 twice as fast. |
| statmods | all 0 | str / dex / con / int / wis / chr adjustments to the rolled statline. |
| groups | [] | Trait groups (Arcane, Wild, Stout, …) - free-form strings that class allowed_groups and feat gates match against. |
| frequency | 100 | Relative population weight. Parsed, consumed by nothing yet - reserved for future NPC population rolls. |
| default | false | The starting race players hold before choosing. Exactly one race should say so. |
| perm | (none) | Permission node that locks this entry, LuckPerms-friendly. |
| allow_crafting … allow_taming | true | Station gates - six booleans. A deny from any source wins. |
| allowed_weapons … disallowed_armour | (no statement) | Proficiencies - see Gear below. |
| skills | {} | What this race teaches - see Granting skills. |
| skill_points | 0 | Starting skill points. |
| skill_points_per_level | 0 | Fractional accrual is fine: 0.25 is one point every 4 levels. |
| levels | {} | Milestone rewards - see Level rewards. |
| xp_adjust_kill / _mine / _smelt | 0 | Percentage tweak to class XP from that source; race and class adjustments sum. |
| attributes, enchant_rebate, smith_refund, gold_tool_mana | - | Boons - see below. |
Classes
Classes share the race schema's identity, statmods, gear rules, skills, levels and boons, and add growth (how the class scales the race's base numbers) and eligibility (who may take it, and whether it sits in the main or sub slot).
name: Warlord
description: "Somebody has to point the sword."
health_mod: 4
health_per_level: 0.5
mana_bonus: 0
statmods: { str: 1, chr: 1 }
allowed_groups: [Humanoid]
requires_one: [legendquest:fighter, legendquest:ranger]
main_only: true
skills:
legendquest:rally:
level: 5
Stat modifiers from main and sub class combine by the original plugin's rule, kept for balance continuity: same sign, the larger magnitude wins; opposite signs, they sum.
| Field | Default | Meaning |
|---|---|---|
| health_mod | 0 | Flat health on top of the race's base. |
| health_per_level | 0 | Health growth per character level. |
| mana_bonus | 0 | Flat mana on top of the race's pool. |
| mana_per_level | 0 | Mana growth per level. |
| mana_per_second | 0 | Extra regen, added to the race's. |
| speed_mod | 0 | Added to the race's base_speed. |
| allowed_races | [] | Explicit race ids that may take this class. |
| allowed_groups | [] | Trait groups that may. Either list matching opens the door; both empty = open to all. |
| requires | [] | Classes that must ALL be mastered (max level) first. |
| requires_one | [] | Classes of which ANY ONE mastered suffices. |
| main_only / sub_only | false | Restrict which slot the class may occupy. |
| default | false | The starting class (Citizen). |
| titles | {} | Rank names by level - see below. |
A class can name what a character is called at a given level, as distinct from what they can do. A key is the level a title starts at, and it holds until the next one up:
"titles": {
"1": "Squire",
"25": "Man-at-Arms",
"50": "Knight",
"100": "Champion",
"140": "Lord"
}
A blank title is treated as no title, which is how a class opts out of the bottom band - "Squire" is
worth having, "Level 1 Citizen" is not. Titles feed the nameplate
({title}) and, where
Standards
is installed, decorate chat too - entirely optional, and everything above works exactly the same
without it.
Skills
A skill's effects list runs in order, and each entry picks its own target.
name: Blink
description: "Elsewhere, immediately."
icon: minecraft:ender_pearl
type: active
cooldown: 6000
mana_cost: 5
effects:
- { type: "legendquest:teleport", max_range: 24 }
| Field | Default | Meaning |
|---|---|---|
| name | (required) | Display name. |
| description | (none) | Tooltip and handbook text. |
| icon | minecraft:enchanted_book | Item id drawn in the panel and HUD. A plain string on purpose: a typo'd icon falls back to the book instead of failing the world load. |
| type | active | active (player fires it), passive (always on, re-applied on a tick), triggered (fires from combat events). |
| buildup | 0 | ms of wind-up before the effects land - telegraphed casting. |
| delay | 0 | ms between buildup and landing. |
| duration | 0 | ms the skill counts as active (drives the HUD's green to amber to red bar and the 5-second fade warning). |
| cooldown | 0 | ms after that before it is ready again. Phases run READY, BUILDING, DELAYED, ACTIVE, COOLDOWN. |
| mana_cost | 0 | Mana per use. |
| karma_cost | 0 | Karma spent per use. |
| karma_required | 0 | Positive: karma must be at least this. Negative: must be at least this evil. |
| karma_reward | 0 | Karma granted on use - casting Darkness should probably cost your soul something. |
| consumes / consumes_qty | (none) / 1 | An item eaten per use. |
| xp | 0 | Class XP awarded on a successful use. |
| trigger | (none) | For triggered skills - see below. |
| passive_interval | 3000 | ms between re-applications of a passive's effects. |
| effects | [] | The list that IS the skill. |
Triggers: trigger: { on: melee_hit, chance: 25.0 }.
on is one of melee_hit (you land a hit; trigger target is
the victim), hurt (you take damage; target is the attacker),
kill (target is the victim), or fall (landing, before
damage applies). chance is a percentage, default 100.
Targets
Most effects take a target block, with a sensible per-effect default so simple YAML stays simple.
| Kind | Meaning |
|---|---|
| self | The caster. |
| looking_at | The living entity in the crosshair, within range (default 16). |
| nearby | Every living entity within radius (default 4) of the caster, not the caster. |
| trigger | The other party of the trigger event - the mob you hit, the thing that hurt you. |
| party | The caster AND party members within range, defaulting to the party XP-share range, so songs and blessings reach exactly as far as shared glory does. No party = just the caster. |
Effects
Compose freely. New types come from skill-pack mods - see writing a skill pack.
| Effect | Options |
|---|---|
| legendquest:damage | amount (required), target (default looking_at). Magic damage. |
| legendquest:heal | amount (required), target (default self). Hearts and heart particles. |
| legendquest:potion_effect | effect (required), duration (ms, default 5000), amplifier (0), particles (true - set false on always-on passives, nobody wants to live in a lava lamp), show_icon (true), target (self). The workhorse for auras, hexes, songs and passives. |
| legendquest:leap | power (1.5), lift (0.6). Launch the caster along their look. |
| legendquest:teleport | max_range (32). Blink to the looked-at block, with portal particles and the enderman noise. |
| legendquest:lightning | visual_only (false - true is all flash, no damage), target (looking_at). |
| legendquest:summon | entity (required), count (1), target (looking_at). |
| legendquest:message | text (required), action_bar (true; false = chat). & colours supported. |
| legendquest:ignite | duration (ms, 5000), target (looking_at). |
| legendquest:give_item | item (required), count (1). Overflow drops at the caster's feet. |
| legendquest:sound | sound (required), volume (1.0), pitch (1.0), stop_after (ms, 0 = let it ring). stop_after exists because a music disc outlives any skill by minutes - the Bard's Battle Hymn plays Pigstep with stop_after matching its duration, and the cut is a vanilla stop-sound packet, so it works on vanilla clients too. |
| legendquest:particle_line | particle (minecraft:end_rod), per_block (density, 4), target (looking_at). The visual language of "I cast a thing at you". |
| legendquest:projectile | entity (required), speed (1.5), power (1, fireballs only). Fireballs get their real constructors; anything else spawns with speed as straight velocity. |
| legendquest:run_command | command (required), undo_command (none), duration (ms until the undo runs). The old permskills, modernised: run any command under LegendQuest's costs and cooldowns. Runs as the caster with gamemaster permission, output silenced; %player% expands to their name. Temp flight is exactly command + undo_command + duration. |
The shared parts
Races, classes and feats all grant skills the same way - a map from skill id to the terms of the
grant: level (default 0), cost in skill points
(default 0 = automatic), and an optional karma_min /
karma_max soul band.
skills:
legendquest:holy_light:
level: 12 # character level required
cost: 0 # skill points to buy; 0 = free at level
karma_min: 50 # only the good may hold the light...
legendquest:darkness:
level: 12
karma_max: -50 # ...and only the wicked may quench it.
Karma bands make paired good/evil choices real: grant both sides to one class and each character can only ever hold one. Drifting out of a band suspends the skill - greyed with a purple [soul] note, unusable, skipped when cycling the loadout - rather than refunding it. Redemption and corruption both do exactly what they say, in both directions.
Milestone bonuses, cumulative up to the player's current level. Keys are levels; values are what
arrives. hp, mana, sp (skill
points), manaregen and stats: { str: … } all work.
levels:
10: { hp: 5 }
50: { dex: 1, hp: 5 }
100: { hp: 5, mana: 5, sp: 5, manaregen: 2.5 }
The balancing weights behind flavourful restrictions - innate perks a race, class or feat grants just by being what it is. Race and class boons stack additively.
attributes:
minecraft:armor_toughness: { base: 1.0, per_level: 0.05 }
minecraft:luck: { base: 0.5 }
enchant_rebate: 1 # XP levels handed back after enchanting
smith_refund: 0.2 # chance to recover a material when crafting gear
gold_tool_mana: 0.5 # golden tools harvest like netherite, per-block mana cost
attributes takes any attribute id, including other mods', with a flat
base and an optional per_level ramp. Ids are
deliberately not codec-validated: a typo costs you the boon, not the world load.
Six fields control proficiency: allowed_weapons,
disallowed_weapons, allowed_tools,
disallowed_tools, allowed_armour,
disallowed_armour.
allowed_weapons: "#legendquest:mage_weapons" # tag form: ONE string
allowed_tools: ["minecraft:bow", "minecraft:crossbow"] # list form: ids only
A tag is only legal in the bare string form. Each field takes either a single item tag as a string or a list of item ids - a tag inside a list fails registry load, because vanilla's registry-list codec rejects it. Need a tag plus extra ids? Make a datapack item tag that includes both (tags may contain other tags) and reference that.
The semantics are the original plugin's, worth restating because they are subtle:
Getting it wrong hurts, never confiscates: wrong weapons hit like fists (fumble), wrong tools dig
slow, wrong armour slows and tires. Stations are six booleans, all defaulting to true -
allow_crafting, allow_smelting,
allow_brewing, allow_enchanting,
allow_repairing, allow_taming. A deny from any source
wins, and the handbook prints barred stations on every race and class page.
Feats
Bought with skill points, and it reuses every engine above - boons for passives, gear rules for proficiencies, skill grants for actives. One file can say "+1 toughness", "may wear heavy armour" or "learns Blink", or all three, with zero new code. This is what makes two level-10 elf fighters different people.
name: Toughness
description: "Hard to put down."
icon: minecraft:shield
cost: 8
level: 5
requires: [legendquest:battle_hardened]
allowed_groups: [Humanoid]
attributes:
minecraft:max_health: { base: 4 }
| Field | Default | Meaning |
|---|---|---|
| name | (required) | Display name. |
| description | (none) | Handbook prose. |
| icon | minecraft:nether_star | Panel and handbook icon. |
| cost | 5 | Skill points. |
| level | 0 | Minimum character level. |
| karma_min / karma_max | (unbounded) | Soul band; drifting out suspends the feat's benefits, exactly like skill grants. |
| requires | [] | Feat chain - all listed feats must be owned first. |
| allowed_races / allowed_groups / allowed_classes | [] | Eligibility; empty = everyone. |
| attributes / enchant_rebate / smith_refund / gold_tool_mana | - | Boons. |
| skills | {} | Skill grants. |
| allowed_* / disallowed_* | (no statement) | Gear proficiencies. |
Traps worth knowing about
Registries freeze at world load. /reload covers item tags and messages.yml only. The classic symptom of forgetting is that your YAML edit "did nothing".
A registry parse error can boot the server fine and then fail the client's world load. If a fresh entry crashes people on join, suspect the newest data file, not the network.
Gear proficiency fields take either one tag as a bare string or a list of plain ids. A tag inside a list fails registry load. The README example was wrong about this once.
minecraft:dust and friends carry options and need vanilla's object form.
consumes, effect, entity, item and sound ARE validated - a typo fails the entry at load. icon and boon attributes deliberately are not, and fail soft.
cooldown: 6 is six thousandths of a second, and the panel will cheerfully show you a skill with no cooldown.
Packing a datapack ZIP?
pack.mcmeta and data/ must sit at the
zip root. Wrap them in a folder and Minecraft ignores the pack
with no error and no log line at all.