Cast Modes & Channeling
Previous: Targeting & Target Selection · Next: Costs, Conditions & Variables
Every skill has a cast mode (cast_mode) that decides, after "the trigger is pressed," how this skill is cast:
- Does it fire immediately on press?
- Or press once to turn on, press again to turn off?
- Or does it have to "channel" for a while, firing only when the channel completes and failing if interrupted partway?
These three behaviors map to three values. This page breaks them down one by one, from the simplest instant cast to the most complex channeled cast.
🖼️ [Image placeholder] A comparison diagram of the three cast modes (instant / toggle / channel) · suggested
assets/cast-modes.png
🎯 Overview of the Three Cast Modes
cast_mode | Name | Behavior | Best for |
|---|---|---|---|
instant | Instant (default) | Fires on press, no channel | The vast majority of skills: fireballs, dashes, AOEs |
toggle | Toggle | Each trigger flips between on / off | Persistent auras, shields, stances, fields |
channel | Channeled cast | Takes effect only after channeling for a while; fails if interrupted partway | Charged ultimates, teleports, channeled abilities |
To write it, just add one line at the top level of the skill yml:
cast_mode: instant # this line is optional; the default is instant⚡ instant (default)
The most common behavior, and the default when cast_mode is omitted.
- Fires on press, with no channel and no delay.
- Cooldown, resource cost, blood sacrifice, GCD… all costs are deducted instantly at the moment the cast succeeds.
- If the gates (unlock / cooldown / resource / condition…) don't pass, it's blocked outright and nothing is deducted.
id: fire_wave
display: "&c火焰波"
cast_mode: instant # can be omitted
cooldown:
base: 3000 # goes on 3-second cooldown immediately after a successful cast
resource:
mana: 15 # deducts 15 mana immediately after a successful castThese skills don't need the rest of this page—reading this far is enough. The two modes below are advanced.
🔁 toggle
Stance / aura skills that "press once to turn on, press again to turn off."
How it works
- First trigger → QS flips the state to on.
- Second trigger → QS flips the state to off.
- And so on.
Key point: QS only maintains the state of "is it currently on or off"; it does not handle the actual on/off effect. QS passes the current state to MythicMobs as the variable toggle_state (value "on" or "off"), and your MM skill decides, based on toggle_state, whether to "add a shield" or "remove the shield."
⚠️ The toggle state is in-memory, stored in
ToggleTracker. A player relog resets it to off; it does not persist.
💡 The toggle "cooldown" is the cooldown per switch, not "how long the shield lasts." Every switch (whether on or off) runs through one cooldown + cost. The persistent effect is maintained on the MM side.
Corresponding placeholder: %qinhskills_<skill>_toggled% (true / false).
Full example: the shield skill (bundled example, copied verbatim)
#==============================================================================
# 护盾 shield —— toggle skill: press once to turn on, press again to turn off
#
# This example teaches you: cast_mode: toggle —— switches between "on/off", rather than ending after one cast.
# QS passes the current toggle state as the variable toggle_state(on/off) to MythicMobs,
# and your MM skill decides whether to "add a shield" or "remove the shield" accordingly.
# Trigger: sneak + left-click | Type: reactive | Category: utility
#
# Note: the toggle "cooldown" is the per-switch cooldown, not the shield duration; the persistent effect is maintained on the MM side.
#==============================================================================
id: shield
display: "&e护盾"
meta:
category: utility
type: reactive # reactive = reactive type (defensive/counter), just a classification label; runtime still follows the top-level type below
trigger:
primary: SHIFT_LEFT_CLICK
state:
required: IDLE
graph:
entry: shield
execution:
mythic_skill: shield
type: active # top level is active (the loader runs the active logic accordingly); meta.type's reactive is only used for classification
max_level: 1
resource:
mana: 20
cooldown:
base: 6000 # cooldown for each "on/off" switch
cast_mode: toggle # ★ key: toggle mode. Each trigger flips on/off and passes toggle_state to MMHow the MM side branches on toggle_state
QS passes over toggle_state, and the MM skill reads it with <skill.var.toggle_state>. Two common approaches:
Approach 1: MM condition check (branching within one skill)
# plugins/MythicMobs/skills/shield.yml
shield:
Skills:
# toggle_state == on → add shield
- skill{s=ShieldOn} @Self ?check{var=toggle_state;value=on}
# toggle_state == off → remove shield
- skill{s=ShieldOff} @Self ?check{var=toggle_state;value=off}Approach 2: split into two sub-skills (clearer)
ShieldOn:
Skills:
- potion{type=ABSORPTION;duration=999999;level=2} @Self
- particle{p=enchantmenttable;amount=40} @Self
ShieldOff:
Skills:
- removepotion{type=ABSORPTION} @SelfThe exact
?check/variable syntax depends on your MM version; this just demonstrates the idea of "branching ontoggle_state." For the full explanation of MM receiving QS variables, see Integrating with MythicMobs.
📿 channel — Channeled Cast
Charged / channeled skills that "after pressing, must channel for a while, firing only when the channel completes and failing if interrupted partway."
Full field table (channel.*)
| Field | Default | Description |
|---|---|---|
time_ticks | — | Channel duration (ticks, 20 = 1 second). Must be > 0 to enter channeling, otherwise it degrades to instant |
bar_type | bossbar | Channel UI: bossbar (top health bar, recommended) / actionbar (text at the bottom) / none (no built-in UI, leave it to BetterHud to draw from placeholders) |
interrupt_on_move | true | Interrupt on movement |
move_threshold | 0.5 | Movement threshold (blocks), exceeding it interrupts; 0 = any movement interrupts |
interrupt_on_damage | true | Interrupt on taking damage |
cost_on_start | false | false = resource is deducted only on completion (no loss if interrupted); true = deducted on start (anti-abuse) |
cooldown_on_start | false | false = goes on CD only on completion; true = goes on CD on start |
When these fields are omitted, they take the global defaults from the
channel.*section inconfig.yml; the skill yml overrides them per item—you only write the few you want to change, and the rest follow the global defaults. For theconfig.ymldefaults, see Configuration File.
The full channeling flow
After the trigger is pressed, QS proceeds in this order:
1. Gates (unlock/cooldown/resource/condition… including the resource-sufficiency check)
↓ all pass
2. If cost_on_start: true → deduct resource now
3. If cooldown_on_start: true → go on cooldown now
↓
4. Start the ChannelManager countdown (time_ticks)
↓ check for interruption every tick:
· movement > move_threshold (when interrupt_on_move is enabled)
· taking damage (when interrupt_on_damage is enabled)
· disconnect / death / world change
↓
┌──────────────┬──────────────────────────┐
│ Channel done │ Interrupted partway │
├──────────────┼──────────────────────────┤
│ If not yet │ Notify "§7[QS] §c吟唱被打断" │
│ deducted on │ Not deducted on start → no │
│ start → deduct │ deduction; already deducted │
│ resource now, │ on start → already lost (this│
│ go on CD, fire│ is the anti-abuse cost) │
│ () to call MM │ │
└──────────────┴──────────────────────────┘💡 How to choose
cost_on_start? To let players "lose nothing if interrupted" (friendlier), keep the defaultfalse; to prevent players from repeatedly starting to scam resources / stall CD (more hardcore), settrue.cooldown_on_startworks the same way.
UI text during channeling
bossbar: top health-bar text§e吟唱 §f{display name} §7{percentage}%, the bar fills with progress.actionbar: a progress bar at the bottom of the screen▰▰▰▱▱.none: QS draws nothing, only updates the placeholders, leaving it to HUD plugins like BetterHud to draw.
The unified interrupt notice: §7[QS] §c吟唱被打断.
Corresponding placeholders:
| Placeholder | Meaning |
|---|---|
%qinhskills_<skill>_channeling% | Whether this skill is currently being channeled (true/false) |
%qinhskills_<skill>_channel_progress% | Channel progress percentage |
Full example: demo_slash_charged, the demo charged slash (bundled example, copied verbatim)
#==============================================================================
# 演示蓄力斩 demo_slash_charged —— channeled cast skill (channel)
#
# This example teaches you: cast_mode: channel —— after pressing, it must "channel" for a while before firing,
# and being interrupted during the channel (movement/damage) fails it. Good for charged ultimates, teleports, and channeled skills.
# Trigger: sneak + right-click | Type: active | Category: combat
#
# Channel progress goes to the top BossBar by default; to leave it to BetterHud to draw, set bar_type to none and read the placeholders.
# ⚙ This skill also ships with the server by default and auto-unlocks (config.yml unlock.starter_skills).
#==============================================================================
id: demo_slash_charged
display: "&b演示蓄力斩"
meta:
category: combat
type: active
rank: basic
trigger:
primary: SHIFT_RIGHT_CLICK
state:
required: IDLE
graph:
entry: demo_slash_charged
execution:
mythic_skill: demo_slash_charged
type: active
max_level: 1
cooldown:
base: 3000
resource:
mana: 10
cast_mode: channel # ★ key: channeled cast mode
channel:
time_ticks: 40 # channel duration: 40 ticks = 2 seconds (20 ticks = 1 second)
bar_type: bossbar # channel display: bossbar top health bar (recommended) / actionbar text at bottom of screen / none leave it to an external HUD
interrupt_on_move: true # interrupt on movement
move_threshold: 0.5 # only counts as "moved" if displacement exceeds 0.5 blocks (slight animation jitter won't interrupt)
interrupt_on_damage: true # interrupt on taking damage
cost_on_start: false # false = deduct resource only when the channel completes (no loss if interrupted); true = deduct on start (anti-abuse)
cooldown_on_start: false # false = go on cooldown only on completion; true = go on cooldown on startOnly after the channel completes does QS go and fire() the MM skill of the same name, demo_slash_charged—so the MM side's setup is no different from a normal instant skill; all the channeling logic is handled here on the QS side.
⚠️ Common Pitfalls
| Symptom | Cause |
|---|---|
Set cast_mode: channel but it fires instantly | channel.time_ticks is missing or set to 0 → degrades to instant. Must be > 0 |
| Toggle skill "won't turn off" after relog | The state is in-memory and resets to off on relog; just press once more to re-enter the toggle cycle |
| Channel interrupts on the slightest movement | move_threshold is too small or set to 0; to tolerate slight animation jitter, keep the default 0.5 |
| Players repeatedly start to farm resources | Set channel.cost_on_start: true to deduct on start |
| The toggle on / off effects don't take effect | QS only passes toggle_state; the actual effect must branch on it on the MM side (see above) |
📚 Continue Reading
- Costs, Conditions & Variables — resource / blood sacrifice / declarative conditions / variable pass-through / level scaling
- Cooldowns, Charges, GCD & Conflicts — how to configure cooldowns, charges, GCD, and conflict groups
- Configuration File — the
channel.*global defaults inconfig.yml - Integrating with MythicMobs — how MM receives QS variables like
toggle_state