Skip to content

Passive Skills (passive)

Previous: Triggers · Next: Targeting and Target Selection


A passive skill = a skill that the player doesn't press a key for; it triggers automatically when a condition is met. Thorns on taking damage, lifesteal on kill, berserk at low health, a buff stack lost every second…—all powered by passives.

💡 Division of labor in one sentence: QS decides "when it triggers," MythicMobs decides "what gets cast when it does." A passive also requires a same-named skill on the MM side to have real effects, otherwise it only sends a placeholder message.

🖼️ [Image placeholder] A diagram of "player takes a hit → ON_DAMAGED event → QS triggers retaliate → MM reflects damage to the attacker" · suggested assets/passive-retaliate.png


1. How to write a passive skill (overview)

A passive only adds three things over an active, and drops one:

  • Adds: top-level type: passive and meta.type: passive (both required).
  • Adds: a passive_triggers list, listing "what event triggers it."
  • Adds: a cooldown_ms rate limit for high-frequency events.
  • Drops: trigger.primary can be omitted—since 1.0.16 the schema auto-handles it as PASSIVE (writing it works too, omitting it is simpler).
yaml
type: passive                    # ★ this single line is what actually makes it passive
meta:
  type: passive                  # also write passive in meta
passive_triggers:                # passive trigger list (multiple allowed; hitting any one triggers it)
  - type: ON_ATTACK              # when an attack lands
    id: retaliate
    cooldown_ms: 0               # rate limit: 0 = no limit; high-frequency events must use a non-zero value
  - type: ON_LOW_HEALTH          # when health drops below the threshold
    id: last_stand
    threshold_pct: 30            # health percentage threshold (0-100), default 30, per-skill

Each passive_triggers entry has three parts:

FieldRequiredMeaning
typetrigger type (one of the 11 in the table below)
ididentifier for this trigger (used to distinguish multiple triggers)
cooldown_msoptionalrate limit: this trigger fires at most once within these milliseconds (default 0 = no limit)
threshold_pctoptionalON_LOW_HEALTH only: health percentage threshold, default 30

Passives don't auto-unlock by default. Before testing, run /qs unlock <skill> first, then go trigger the condition to test the effect.


2. The 11 passive trigger types

Driven by PassiveTriggerListener. Grouped by purpose into three categories:

Combat

typeDriving eventMeaningTarget passed to MMNeeds rate limit
ON_DAMAGEDEntityDamageEventwhen taking damage⚠️ strongly recommended
ON_ATTACKplayer as damagerwhen an attack landsthe victim is passed as @Target / @Triggerdepends on frequency
ON_KILLEntityDeathEventwhen killing a creaturethe dead entity is the targetusually not needed
ON_LOW_HEALTHhealth edgetriggers once when health drops below the thresholdedge-trigger has built-in anti-spam

Behavior

typeTrigger momentMeaningNeeds rate limit
ON_SNEAKstart sneakingwhen sneaking⚠️ strongly recommended
ON_JUMPjumpingwhen jumping (high frequency)⚠️ required
ON_SPRINTstart sprintingwhen sprintingrecommended
ON_BLOCK_BREAKbreaking a blockwhen mining (high frequency)⚠️ required
ON_RESPAWNrespawnafter dying and respawningnot needed
ON_FALLFALL damagewhen taking fall damagedepends

Periodic

typeDriven byMeaningNeeds rate limit
TICKpassive.tick_interval_ticks (config.yml, default 20 ticks = 1 second)triggers once every fixed interval⚠️ spams extremely easily, must be rate-limited via skill cooldown or cooldown_ms

💡 TICK has no idle overhead: when no skill uses a TICK passive, the periodic task idles automatically and incurs no extra performance cost. It only works when there's actually a TICK passive.


3. The edge semantics of ON_LOW_HEALTH (clarified in detail)

Many people think "low-health trigger" = it fires continuously as long as health is low—it doesn't. ON_LOW_HEALTH is an edge trigger, tracking state with lowHealthLatch:

Rule: when health percentage ≤ threshold_pct (source uses <=, so exactly equal counts too) and the skill is not yet latched, it triggers once and records the latch; after that it won't trigger again even if you keep taking damage; only when health recovers to > threshold (strictly above the threshold) is the latch released, and it triggers again next time you drop below.

Edge/latch details: the player gets a low-health check scheduled both on taking damage and on healing—not only on taking damage. Taking damage that pushes health to ≤ threshold triggers and locks; healing that pulls health back to > threshold unlocks (but healing itself doesn't trigger). Thanks to this latch, dropping below once per life casts only once—it won't spam every time you take damage.

In one sentence: health ≤ threshold% (including exactly equal) triggers once; recovery strictly above the threshold resets it.

yaml
passive_triggers:
  - type: ON_LOW_HEALTH
    id: last_stand
    threshold_pct: 30            # triggers when health ≤ 30% (per-skill setting, 0-100)

An example (threshold 30%):

MomentHealth changeTriggers?Note
100% → 35%hasn't dropped below 30% yet
35% → 25%✅ triggers oncethe moment it crosses the threshold
25% → 10%already below the threshold, no repeat trigger
10% → 50%healed above the threshold, latch resets (but healing itself doesn't trigger)
50% → 20%✅ triggers againdrops below again after reset

This is exactly the semantics you want for "near-death berserk / panic ultimate"—dropping below once per life casts only once, and won't spam wildly when taking a combo.


4. High-frequency passives = must rate-limit (must read)

The following triggers fire extremely often; without a rate limit they'll spam in quick succession (message flood, MM skills firing repeatedly):

⚠️ ON_DAMAGED / ON_JUMP / ON_BLOCK_BREAK / ON_SNEAK must be rate-limited with cooldown_ms. Same for TICK—hold the frequency down with the skill's cooldown or cooldown_ms.

yaml
passive_triggers:
  - type: ON_BLOCK_BREAK
    id: mining_buff
    cooldown_ms: 3000            # ★ triggers at most once per 3 seconds, otherwise mining a patch of stone spams it

Two rate-limiting methods (stackable):

  • Per-trigger cooldown_ms: precise to "this one trigger," recommended.
  • Skill-level cooldown.base: the whole skill's cooldown, applies to all triggers.

Settlement timing: high-frequency passive events (ON_DAMAGED / ON_ATTACK / ON_KILL / ON_BLOCK_BREAK, etc.) are all uniformly settled by QS after the MONITOR phase (avoiding events that other plugins cancel)—admins don't need to worry about this part. The only thing you need to do is set a cooldown_ms rate limit on these high-frequency passives.


5. TICK periodic configuration

The TICK interval is controlled centrally in config.yml; all TICK passives share this beat:

yaml
# config.yml
passive:
  tick_interval_ticks: 20        # TICK passive trigger interval (ticks, 20 = 1 second)
                                 # when no TICK passive skill exists, the task idles with no overhead
yaml
# in the skill: a periodic passive triggering once per second (following the beat above)
type: passive
meta: { type: passive }
passive_triggers:
  - type: TICK
    id: regen_aura
    cooldown_ms: 1000            # even with a 1-second beat, adding cooldown_ms is recommended as a safety net

6. Reproduction: retaliate.yml (the standard passive skill example)

This is QS's bundled passive example skills/combat/retaliate.ymlcopy it as is:

yaml
#==============================================================================
#  Thorns Retaliation retaliate  ——  passive skill: automatically counterattacks when attacked
#==============================================================================

id: retaliate
display: "&c荆棘反击"

meta:
  category: combat
  type: passive                 # passive: both type fields must say passive
state:
  required: IDLE
graph:
  entry: retaliate
execution:
  mythic_skill: retaliate
# a passive skill can omit trigger.primary —— the schema auto-handles it as PASSIVE

type: passive                   # ★ this single line is what actually makes it passive
max_level: 1

cooldown:
  base: 0                       # skill-level cooldown; for passive rate-limiting, each trigger's own cooldown_ms is preferred

# passive trigger list: write multiple; hitting any one triggers this skill
passive_triggers:
  - type: ON_DAMAGED            # when taking damage
    id: thorns
    cooldown_ms: 1500           # ★ rate limit: at most once per 1.5 seconds, to avoid retaliation spam during a combo

Note: this example uses ON_DAMAGED. If you want the "attacker" to be the one retaliated against, switch to ON_ATTACK and pass the attacker as @Trigger to MM (see the next section).


7. How the MM side receives a passive (@Trigger / @Target)

A passive is only the "trigger"; the real effect goes in a same-named MythicMobs skill. The key is how to get the "trigger target":

  • ON_ATTACK: the victim is passed as @Target / @Trigger.
  • ON_KILL: the dead entity is passed as the target.
  • To make the attacker take the retaliation: use ON_ATTACK and settle damage on @Trigger in MM.
yaml
# the same-named skill retaliate under plugins/MythicMobs/skills/
retaliate:
  Skills:
    - damage{amount=4} @Trigger          # deal damage to the trigger target (the attacker)
    - particles{p=crit;amount=20} @Trigger

One iron rule runs through the ecosystem: QS does not bake in damage values. The damage{} value is ultimately settled on the MM side by attribute plugins like AttributePlus.


8. Common passive pitfalls

SymptomCauseFix
Passive doesn't trigger at allnot unlocked/qs unlock <skill>
Passive only sends a placeholder message, no effectno same-named skill in MMadd the retaliate skill on the MM side
Wild spam when jumping / mininghigh-frequency trigger not rate-limitedadd cooldown_ms to that trigger
Low-health skill fires every time you take damagemistaken for a threshold triggerit's an edge trigger, this is expected; adjust threshold_pct to change frequency
type written in only one placeboth meta and top-level must say passiveadd type: passive in both places
@Trigger in MM can't get the attackerused ON_DAMAGEDswitch to ON_ATTACK

Further reading