Skill Definition: All Fields
Previous: Skill File Structure · Next: graph & combos
This page is a field-by-field reference manual for the skill definition file (skills/<category>/xxx.yml). All fields are grouped by function, with one table + a small example per group. While configuring a skill, jump to the relevant section whenever you get stuck.
Tip
💡 No need to read it all at once. For your first skill, you only need to write id / display / meta / trigger / type / cooldown to get it running (see the "minimal working template" at the end). Add the rest as needed.
The fields on this page reflect what SkillDefinitionLoader actually loads, corresponding to QS 1.0.22.
🖼️ [Image placeholder] A diagram of skill yml field groups (basic / trigger / gating / target / cost / advanced) · suggested
assets/field-groups.png
🔑 One rule running through the whole page: aliases and priority
Many fields have two ways to write them: a nested block (e.g. cooldown.base) and a top-level alias (e.g. cooldown_ms). Both work, but when both are present, who wins follows a fixed priority:
| Nested form (wins) | Top-level alias | Who wins |
|---|---|---|
cooldown.base / cooldown.group / cooldown.charges | cooldown_ms / cooldown_group / charges | Nested block wins |
cost.health / cost.hunger | health_cost / hunger_cost | Nested block wins |
gcd.triggers_ms / gcd.ignore | gcd_ms / ignore_gcd | Nested block wins |
execution.mythic_skill | mythic_skill (top-level) | execution.mythic_skill wins |
Caution
⚠️ Don't write both and then wonder why your change had no effect. Remember one sentence: the nested block / execution.mythic_skill always wins. The examples on this page all use the nested form.
1️⃣ Basic identity
| Field | Type | Default | Description |
|---|---|---|---|
id | String | filename | Unique skill id, all lowercase; everything else (items / combos / commands) references it |
display | String | = id | Display name, supports & color codes |
type | active / passive | active | This top-level line is what actually decides active / passive; the loader reads it to set SkillType |
max_level | Int | 1 | Maximum level, paired with levels to build growth curves |
tags | List | [] | Tags purely for display / search |
id: fire_wave
display: "&c火焰波"
type: active # ← this line decides active/passive (write passive for passive skills)
max_level: 5
tags: [fire, aoe, combat]2️⃣ Ecosystem info (meta / trigger / state / graph / execution)
This set of fields feeds the /qs reload consistency check and serves as a reference for combo orchestration. A missing meta raises a schema warning.
| Field | Values | Description |
|---|---|---|
meta.category | combat/movement/utility/boss/combo | Category, must match the folder the file is in |
meta.type | active/passive/reactive | Label only (what actually takes effect is the top-level type) |
meta.rank | basic/advanced/elite/boss | Display-only tier |
trigger.primary | TriggerType | Primary trigger key, must be in the graph entry node's triggers |
state.required | SkillState | State required to cast, defaults to IDLE; must match the entry node's require_state |
graph.entry | String | Which graph to use (found by graph_id) |
execution.mythic_skill | String | The MM skill actually executed, highest priority (> top-level mythic_skill) |
meta:
category: combat
type: active # label, classification only
rank: basic # display only
trigger:
primary: RIGHT_CLICK # must appear in the graph entry node's triggers
state:
required: IDLE # must match the entry node's require_state
graph:
entry: fire_wave # found by graph_id
execution:
mythic_skill: fire_wave # the MM skill actually executed (takes priority over top-level mythic_skill)For all TriggerTypes and graph node details, see graph & combos.
3️⃣ Cooldown / charges / cooldown group (cooldown)
| Field (nested / alias) | Type | Default | Description |
|---|---|---|---|
cooldown.base / cooldown_ms | Long(ms) | 0 | Cooldown duration, in milliseconds. 3000 = 3 seconds |
cooldown.group / cooldown_group | String? | none | Cooldown group: skills in the same group share a cooldown |
cooldown.charges / charges | Int | 1 | Charge stacks; when >1 it replaces the binary cooldown, recovering one stack at a time per base |
cooldown:
base: 3000 # 3-second cooldown
group: fire # same group (e.g. all fire skills) shares a cooldown
charges: 3 # 3 charges: after spending one it recovers, refilling one stack every 3 secondsTip
💡 Binary cooldown vs charges: leaving charges unset or = 1 gives the ordinary "ready / on cooldown" binary state; charges > 1 switches to a charge system of "you can cast as many times as you have stacks," each stack recovering over base time. See Cooldown / Charges / GCD & Conflicts for details.
Caution
⚠️ cooldown.base has a prerequisite — it requires meta.category. The entire meta.* / trigger.* / graph.* / execution.* ecosystem info (including the cooldown.base nested block) is only parsed when the skill file contains meta.category — when SkillSchemaParser sees no meta.category, it returns null immediately. Without meta.category, cooldown.base has no effect and you must use the top-level cooldown_ms instead.
Also: when a levels.N omits its cooldown, it only falls back to the top-level cooldown_ms, not to cooldown.base. So if you define cooldown via cooldown.base but some level doesn't write cooldown_ms, that level gets 0 (no cooldown).
Recommendation: either write both meta.category + cooldown.base, or stick to the top-level cooldown_ms and write cooldown_ms explicitly in every level.
(Note: all bundled examples include meta.category, so the cooldown.base in the examples all work correctly.)
4️⃣ Resource cost (resource)
| Field | Type | Description |
|---|---|---|
resource.<key> | Map | Resource cost, e.g. mana: 15 |
resource:
mana: 15 # casting costs 15 manaCaution
⚠️ About mana: resource pools like mana will eventually be managed by QinhClass (QC); for now they're a temporary placeholder. Use it like this for now — once QC takes over, the skill yml won't need to change. See the responsibility boundaries in Core Concepts.
5️⃣ Cast mode (cast_mode)
| Value | Meaning |
|---|---|
instant (default) | Instant cast |
toggle | Toggle (press again to turn off) |
channel | Channeled cast bar (charge-up bar) |
cast_mode: instant # instant / toggle / channelFor the full channel (
channel) fields see §10 below; for toggle (toggle) details see Cast Modes & Channeling.
6️⃣ Target & targeting (target)
target can be written as a scalar (mode only) or a block (with range / filter, etc.).
Scalar form:
target: NEAREST # write the mode directlyAvailable modes: SELF (yourself) / LOOK (crosshair ray) / NEAREST (closest) / FARTHEST (farthest) / LOWEST_HP (most wounded, for finishing) / HIGHEST_HP (tankiest) / RANDOM (random).
Block form (more granular):
| Field | Default | Description |
|---|---|---|
target.mode | SELF | One of the modes above |
target.range | 30 | Targeting radius (blocks), range 1–100 |
target.filter | LIVING | ANY / LIVING / MONSTERS (mobs only) / PLAYERS (players only) / NOT_PLAYERS (non-player creatures) |
target.required | false | true = if nothing is locked, the cast fails ("no available target" prompt) |
target.require_los | false | true = only lock targets with line of sight (not through walls) |
target:
mode: NEAREST # pick the closest
range: 6 # within 6 blocks
filter: MONSTERS # mobs only
required: true # fails if no target
require_los: true # only lock visible ones (not through walls)Tip
💡 The target QS picks is passed to the MM skill as @Target. require_los mainly affects NEAREST (which locks through walls by default); LOOK is a ray to begin with. See Target & Targeting for the full explanation.
7️⃣ Global cooldown GCD (gcd)
| Field (nested / alias) | Type | Default | Description |
|---|---|---|---|
gcd.triggers_ms / gcd_ms | Long(ms) | 0 | Duration of a brief lockout of all character skills after casting; 0 = doesn't trigger GCD |
gcd.ignore / ignore_gcd | Boolean | false | true = this skill is not subject to the GCD |
gcd:
triggers_ms: 800 # for 0.8s after casting, all skills are blocked
ignore: false # whether this skill itself is subject to the GCDTip
💡 Damage skills often set triggers_ms to prevent firing several skills in a single frame; instant mobility / interrupt skills often set ignore: true so they aren't locked by another skill's GCD.
8️⃣ Health / hunger cost (cost)
Uses vanilla health / hunger as the cost (not the QC mana pool), suitable for "blood-sacrifice" skills.
| Field (nested / alias) | Type | Description |
|---|---|---|
cost.health / health_cost | Double | Health deducted, half a heart = 1; if health ≤ this value the cast fails, and after deduction it floors at 0.5 |
cost.hunger / hunger_cost | Int | Hunger deducted |
cost:
health: 4.0 # deduct 2 hearts; fails if health is insufficient, leaves at least 0.5
hunger: 2 # deduct 2 hunger9️⃣ Cooldown ready notify (ready_notify)
Sends an actionbar + sound the instant the cooldown ends.
| Field (nested / alias) | Default | Description |
|---|---|---|
ready_notify.enabled / ready_notify: true | false | Toggle |
ready_notify.sound | block.note_block.pling | Sound id (dots / underscores both fine) |
ready_notify.message | &a{skill} &7已就绪 | Actionbar text, {skill} = display name |
ready_notify:
enabled: true
sound: block.note_block.pling
message: "&7{skill} &f已就绪" # {skill} is replaced with the display nameCaution
⚠️ Only applies to binary-cooldown skills; not applicable to charge skills (charges > 1).
🔟 Channeled cast bar (channel)
Only takes effect with cast_mode: channel; channeling only truly begins when time_ticks > 0, otherwise it degrades to instant cast.
| Field | Default | Description |
|---|---|---|
channel.time_ticks (alias channel.ticks) | 0 | Cast bar duration (ticks, 20 = 1 second), >0 to enter channeling; the alias channel.ticks is equivalent |
channel.bar_type | bossbar | Cast bar UI: bossbar (top) / actionbar (text below) / none |
channel.interrupt_on_move | true (from config) | Interrupt on movement |
channel.move_threshold | 0.5 | Movement threshold (blocks); exceeding it interrupts |
channel.interrupt_on_damage | true | Interrupt on taking damage |
channel.cost_on_start | false | true = deduct resources at the start (anti-spam); false = deduct on completion |
channel.cooldown_on_start | false | true = enter cooldown at the start; false = enter cooldown on completion |
cast_mode: channel
channel:
time_ticks: 40 # 2-second cast bar
bar_type: bossbar # top boss-bar style progress
interrupt_on_move: true # interrupt on moving
move_threshold: 0.5
interrupt_on_damage: true # interrupt on taking damage
cost_on_start: false # only deduct on completion (no loss if interrupted)
cooldown_on_start: false # only enter cooldown on completionFor the full channeling mechanics see Cast Modes & Channeling.
1️⃣1️⃣ Cast conditions (conditions)
A declarative list of prerequisites; the skill casts only when all are met; an empty list = no restrictions.
conditions:
- "player_level:>=5" # player level ≥ 5
- "player_in_world:world" # in the main world
- "player_health_pct:>=50" # health ≥ 50%
- "has_target:true" # has a target
- "target_type:ZOMBIE" # target is a zombie
- "target_distance:<=10" # target within 10 blocksThe syntax is key:value, where the value may carry a comparator >= <= == != > < = (defaults to =). Unknown keys are always true (a typo won't lock the skill out). Overview of available keys:
| Key | Description |
|---|---|
player_level | Player level |
player_health / player_health_pct | Absolute health / percentage (0–100) |
player_food | Hunger value |
player_in_world | World name |
player_has_permission | Permission node |
player_gamemode | SURVIVAL / CREATIVE… |
player_sneaking / player_sprinting / player_on_fire / player_on_ground | Boolean |
player_y | Y coordinate |
has_target | Whether there's a target |
target_type | Target entity type (e.g. ZOMBIE) |
target_distance | Distance to the target |
For full condition usage see Cost / Conditions / Variables. For complex logic, use
script.pre_jsin §15 below.
1️⃣2️⃣ Conflict groups & combo markers
| Field | Type | Description |
|---|---|---|
conflict_groups | Set<String> | Conflict group: after casting, same-group skills are briefly unavailable, for a window of gate.conflict_window_ms (default 1000ms) |
combo_group | String? | Combo grouping marker |
conflict_groups:
- melee_burst # for about 1 second after casting, other same-group skills can't be cast
combo_group: fire_combo # combo grouping marker1️⃣3️⃣ Variable passthrough (variables)
Passes custom key-value pairs through to MM skill variables <skill.var.key>.
variables:
element: fire # in MM, read "fire" via <skill.var.element>QS also auto-injects built-in variables like
mode/source/slot/player/origin; see Cost / Conditions / Variables for details.
1️⃣4️⃣ Per-level overrides (levels)
Override cooldown / cost / params by level. params are passed through to MM (read in MM via <skill.var.power>).
levels:
1:
cooldown_ms: 3000
resource: { mana: 15 }
params: { power: "1.0" } # level-1 power
2:
cooldown_ms: 2800
resource: { mana: 18 }
params: { power: "1.2" } # upgrade: shorter cooldown, higher power| Sub-field | Description |
|---|---|
levels.N.cooldown_ms | Cooldown for that level |
levels.N.resource | Resource cost for that level |
levels.N.params | Numeric params passed through to MM (MM decides how to use them) |
1️⃣5️⃣ Script hooks (script)
| Field | Description |
|---|---|
script.pre_js | Runs before casting; returning false blocks the cast |
script.post_js | Runs after a successful cast (side effects) |
script:
pre_js: "qinhskills:demo.js:canCast" # returning false means the cast fails
post_js: "qinhskills:demo.js:onCast" # runs effects after a successful castFor how to write scripts see Scripting.
1️⃣6️⃣ Trigger sources (active_triggers / passive_triggers)
Active trigger sources active_triggers, defaulting to [QI_ACTION] (QinhItems item trigger):
active_triggers:
- type: KEY_SLOT # KEY_SLOT / QI_ACTION / COMMAND / API
slot: 1 # optional; only meaningful for KEY_SLOT (which skill slot to bind to)
- type: QI_ACTION # default; QinhItems item trigger| Field | Type | Default | Description |
|---|---|---|---|
type | Enum | QI_ACTION | Trigger source: KEY_SLOT / QI_ACTION / COMMAND / API |
slot | Int | none | Optional, only meaningful for type: KEY_SLOT — which skill slot to bind to |
| type | Trigger source |
|---|---|
KEY_SLOT | Key-bound skill slot (use slot to specify the slot) |
QI_ACTION | QinhItems item key (default) |
COMMAND | Command bridge /qs cast |
API | External plugin API |
Passive triggers passive_triggers (only for type: passive skills). Each entry supports the following fields:
| Field | Type | Default | Description |
|---|---|---|---|
type | Enum | required | Passive trigger type (on damaged / attack / kill / low health…) |
id | String | = type lowercased | Identifier for this passive |
cooldown_ms | Long(ms) | 0 | Rate-limit cooldown for this passive; be sure to set it for high-frequency passives |
threshold_pct | Double | 30.0 | Health percentage threshold; only used by threshold-type triggers like ON_LOW_HEALTH |
type: passive # write passive at the top level too
passive_triggers:
- type: ON_DAMAGED # triggers on taking damage
id: thorns
cooldown_ms: 1500 # always rate-limit high-frequency passives
- type: ON_LOW_HEALTH # low-health trigger (threshold type)
id: last_stand
threshold_pct: 30.0 # only triggers when health ≤ 30%For the full description of all 11 passive triggers (on damaged / attack / kill / low health / sneak / jump / sprint / mine / respawn / fall / periodic), see Passive Skills.
📊 Priority quick reference
When both nested and alias forms are written, who takes effect:
cooldown.base / .group / .charges > cooldown_ms / cooldown_group / charges
cost.health / .hunger > health_cost / hunger_cost
gcd.triggers_ms / .ignore > gcd_ms / ignore_gcd
execution.mythic_skill > mythic_skill (top-level)Mnemonic: the nested block always wins; execution.mythic_skill always wins.
📄 Worked examples (annotated field by field)
Example A: fire_wave — the most basic active skill
id: fire_wave # unique skill id, all lowercase; defaults to filename if omitted
display: "&c火焰波" # display name, supports & color codes
#--- Ecosystem info: category filing + reload consistency check ---
meta:
category: combat # one of the five fixed categories
type: active # classification label: active / passive / reactive
rank: basic # tier label (display only)
trigger:
primary: RIGHT_CLICK # primary trigger key, must be in the graph entry node's triggers
state:
required: IDLE # state required to cast. Must match the entry node's require_state
graph:
entry: fire_wave # which graph to use (found by graph_id)
execution:
mythic_skill: fire_wave # the MM skill actually executed; must match the entry node's mythic_skill
#--- Runtime fields ---
type: active # this line is what actually decides active/passive
max_level: 5 # max level, paired with levels to build a growth curve
cooldown:
base: 3000 # 3-second cooldown
resource:
mana: 15 # casting cost (⚠ mana will eventually be QinhClass's; temporary placeholder)
cast_mode: instant # instant / channel / toggle
# Per-level values: each level takes different cooldown/cost, and passes params through to MythicMobs
levels:
1:
cooldown_ms: 3000
resource: { mana: 15 }
params: { power: "1.0" } # level-1 power (read in MM via <skill.var.power>)
2:
cooldown_ms: 2800
resource: { mana: 18 }
params: { power: "1.2" } # upgrade: shorter cooldown, higher power
# Custom variables passed through to MythicMobs (read in MM via <skill.var.element>)
variables:
element: fire
tags: [fire, aoe, combat] # custom tags, purely display/searchExample B: blade_slash — targeting + health cost + GCD + conditions + ready notify + conflict
id: blade_slash
display: "&7刃斩"
meta:
category: combat
type: active
rank: advanced
trigger:
primary: LEFT_CLICK # left-click trigger (must match the graph entry node's triggers)
state:
required: IDLE
graph:
entry: blade_slash
execution:
mythic_skill: blade_slash
type: active
max_level: 3
cooldown:
base: 2500
# Auto-targeting: QS picks the target on cast and passes it to MM as @Target
target:
mode: NEAREST # NEAREST closest / FARTHEST / LOWEST_HP finishing / HIGHEST_HP / RANDOM
range: 6 # targeting radius (blocks)
filter: MONSTERS # ANY / LIVING / MONSTERS mobs only / PLAYERS / NOT_PLAYERS
required: true # true = cast fails if no target is locked
require_los: true # true = only lock targets with line of sight (not through walls)
# Cost: uses health and hunger, no mana (no resource: block, pure blood sacrifice)
cost:
health: 2.0 # deduct 2 health (1 point = half a heart); fails if insufficient, floors at 0.5
hunger: 1 # deduct 1 hunger
# Global cooldown (GCD): for 0.8s after this skill, casting any skill is blocked
gcd:
triggers_ms: 800
# Cooldown ready notify: sends actionbar + sound the instant cooldown ends (binary-cooldown skills only)
ready_notify:
enabled: true
sound: block.note_block.pling
message: "&7{skill} &f已就绪" # {skill} is replaced with the display name
# Conflict group: for a short time after casting (default 1s), other same-group skills can't be cast
conflict_groups:
- melee_burst
# Cast prerequisites: casts only if all are met (a mistyped key is treated as always true, won't lock the skill)
conditions:
- "player_level:>=3" # player level ≥ 3
- "has_target:true" # must have a target (double safeguard with target.required)
levels:
1: { cooldown_ms: 2500 }
2: { cooldown_ms: 2200 }
3: { cooldown_ms: 2000 }🧱 Ready-to-use templates
Minimal working skill (copy, rename, and run)
id: my_skill
display: "&a我的技能"
meta:
category: combat
type: active
trigger:
primary: RIGHT_CLICK
state:
required: IDLE
graph:
entry: my_skill
execution:
mythic_skill: my_skill
type: active
cooldown:
base: 2000You also need a minimal companion graph; see the entry node example in graph & combos.
Full-field template (trim as needed)
id: full_demo
display: "&6完整示例"
meta: { category: combat, type: active, rank: advanced }
trigger: { primary: RIGHT_CLICK }
state: { required: IDLE }
graph: { entry: full_demo }
execution: { mythic_skill: full_demo }
type: active
max_level: 3
cooldown: { base: 3000, group: fire, charges: 1 }
resource: { mana: 15 }
cast_mode: instant
target: { mode: NEAREST, range: 20, filter: MONSTERS, required: false, require_los: false }
gcd: { triggers_ms: 800, ignore: false }
cost: { health: 0.0, hunger: 0 }
ready_notify: { enabled: true, sound: block.note_block.pling, message: "&a{skill} &7已就绪" }
conditions:
- "player_level:>=3"
conflict_groups: [melee_burst]
combo_group: fire_combo
variables: { element: fire }
script: { pre_js: "", post_js: "" }
levels:
1: { cooldown_ms: 3000, resource: { mana: 15 }, params: { power: "1.0" } }
2: { cooldown_ms: 2800, resource: { mana: 18 }, params: { power: "1.2" } }
3: { cooldown_ms: 2600, resource: { mana: 20 }, params: { power: "1.4" } }
active_triggers:
- type: QI_ACTION
tags: [fire, aoe, combat]Keep reading
- Build combos / configure graph nodes → graph & combos
- Cooldown / charges / GCD / conflict group details → Cooldown / Charges / GCD & Conflicts
- Deep dive on targeting → Target & Targeting
- Channeling / toggle → Cast Modes & Channeling
- The 11 passive skill triggers → Passive Skills
- Cost / conditions / variables → Cost / Conditions / Variables