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.
💡 No need to read it all at once. For your first skill, you only need to write
id/display/meta/trigger/type/cooldownto get it running (see the "minimal working template" at the end). Add the rest as needed.The fields on this page reflect what
SkillDefinitionLoaderactually 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 |
⚠️ Don't write both and then wonder why your change had no effect. Remember one sentence: the nested block /
execution.mythic_skillalways 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 seconds💡 Binary cooldown vs charges: leaving
chargesunset or = 1 gives the ordinary "ready / on cooldown" binary state;charges > 1switches to a charge system of "you can cast as many times as you have stacks," each stack recovering overbasetime. See Cooldown / Charges / GCD & Conflicts for details.
⚠️
cooldown.basehas a prerequisite — it requiresmeta.category. The entiremeta.*/trigger.*/graph.*/execution.*ecosystem info (including thecooldown.basenested block) is only parsed when the skill file containsmeta.category— when SkillSchemaParser sees nometa.category, it returns null immediately. Withoutmeta.category,cooldown.basehas no effect and you must use the top-levelcooldown_msinstead.Also: when a
levels.Nomits its cooldown, it only falls back to the top-levelcooldown_ms, not tocooldown.base. So if you define cooldown viacooldown.basebut someleveldoesn't writecooldown_ms, that level gets 0 (no cooldown).Recommendation: either write both
meta.category+cooldown.base, or stick to the top-levelcooldown_msand writecooldown_msexplicitly in everylevel.(Note: all bundled examples include
meta.category, so thecooldown.basein 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 mana⚠️ About mana: resource pools like
manawill 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)💡 The target QS picks is passed to the MM skill as
@Target.require_losmainly affectsNEAREST(which locks through walls by default);LOOKis 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 GCD💡 Damage skills often set
triggers_msto prevent firing several skills in a single frame; instant mobility / interrupt skills often setignore: trueso 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 name⚠️ 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