Match Clause
Match Clause
Section titled “Match Clause”The match { } block declares the conditions under which an action fires. Every populated field is ANDed together. Empty or null fields are ignored — they aren’t “match anything”, they’re “don’t care”.
A match clause is split into three parts:
- Property fields — direct checks against the Pokémon’s stats and identity. Evaluated server-side as plain comparisons.
- Sugar predicates — a small
sugar { }sub-block of common gates (time, weather, biome, dimension) that compile to Molang at load time. - Raw Molang — an optional
whenMolang = "..."expression for anything else.
ShowdownActions runs them in roughly that order: cheap property checks first, then the compiled Molang. A property mismatch short-circuits the whole evaluation — the Molang never runs if the species was wrong.
match { species = "umbreon" minLevel = 50 sugar { time = "night" } whenMolang = "q.pokemon.shiny || q.world.is_thundering"}That clause matches a level-50+ Umbreon at night that is either shiny or in a thunderstorm.
Property Fields
Section titled “Property Fields”These are evaluated against the live Pokémon. Most are case-insensitive string equality. The integer ones are inclusive bounds.
| Field | Type | Compares against | Re-checked on switch-in? |
|---|---|---|---|
species | String? | Cobblemon species id, e.g. "lycanroc". | Yes |
form | String? | Form name, e.g. "midday", "midnight". | Yes |
type | String? | One of the Pokémon’s elemental types must equal this (lowercase, e.g. "water"). | Yes |
ability | String? | Cobblemon ability id (Showdown-style lowercase, e.g. "swiftswim"). | Yes |
heldItem | String? | Full namespaced item id, e.g. "cobblemon:choice_band". | Yes |
aspect | String? | A required aspect tag, e.g. "shiny", "dusk", "gmax". | No |
nature | String? | Nature id (lowercase, e.g. "modest", "adamant"). | No |
teraType | String? | Tera type (lowercase, e.g. "fire", "stellar"). | Yes |
gender | String? | "male", "female", or "genderless". | No |
minLevel | Int? | Inclusive lower bound (1..100). | Yes |
maxLevel | Int? | Inclusive upper bound (1..100). | Yes |
minFriendship | Int? | Inclusive lower bound (0..255). | No |
isLegendary | Boolean? | Tri-state. true requires the species to be labeled legendary; false requires it not to be; null ignores. | No |
The “re-checked on switch-in” column matters when target = "SELF" or "ALLIES" and the host Pokémon switches out and a different one switches in. a guard is auto-applied around the switch-in hook that re-evaluates these fields on every switch-in, so the effect doesn’t bleed onto a different Pokémon. The static fields aren’t re-checked because their values can’t change mid-battle.
Tera type is in the re-checked column on purpose — a Pokémon can Terastallize partway through a battle and change its Tera type, so a “match the original Tera type” rule wouldn’t be quite right.
Why these specific fields?
Section titled “Why these specific fields?”These are the predicates Showdown’s Pokémon object exposes natively (pokemon.species.id, pokemon.types, pokemon.ability, pokemon.item, pokemon.level, pokemon.teraType). The server-side check is the fast path; the JS guard re-checks the same fields on the Showdown side at every switch-in.
Sugar Predicates
Section titled “Sugar Predicates”The sugar { } sub-block is a small set of common world gates. Each one compiles to a Molang expression at load time, which is then ANDed with whenMolang (if you also set one). They exist because nearly every action that needs a “world condition” gate uses one of these four, and writing them as Molang every time is repetitive.
| Field | Values | Compiles to |
|---|---|---|
time | morning, day, noon, evening, dusk, night, midnight | q.world.is_time('<bucket>') |
weather | clear, rain, thunder, snow | q.world.is_raining, q.world.is_thundering, (!q.world.is_raining && !q.world.is_thundering), depending on the value |
biome | Biome id ("minecraft:forest") or biome tag ("#minecraft:is_forest") | q.world.biome == '...' or q.world.biome_in_tag('...') |
dimension | Dimension id, e.g. "minecraft:overworld", "minecraft:the_nether" | q.world.dimension == '...' |
Sugar predicates are evaluated once at battle start, against the player’s current world position. They aren’t re-checked on switch-in — the world doesn’t move between Pokémon swaps.
The time bucket boundaries are deterministic Minecraft tick ranges:
| Bucket | Tick range (mod 24000) |
|---|---|
morning | 0..2999 |
day | 0..11999 |
noon | 5000..7000 |
evening | 9000..11999 |
dusk | 12000..13000 |
night | 13000..23999 |
midnight | 17500..18500 |
day and morning overlap; night and midnight overlap. That’s intentional — day is “is it daytime at all?”, morning is “is it specifically morning?”. Pick the one that matches what you actually want to gate on.
weather notes
Section titled “weather notes”clear is (!q.world.is_raining && !q.world.is_thundering) — “no rain and no thunder”. Snow is rendered as a separate weather state; weather = "snow" checks q.world.is_snowing directly. Anything outside the four documented values falls back to q.world.weather == '<value>', which works against Cobblemon’s native weather query if the value is one of Cobblemon’s recognized weather names.
biome notes
Section titled “biome notes”If the value starts with #, it’s interpreted as a biome tag and compiled to a q.world.biome_in_tag('...') call. Without the # it’s an exact biome id check. The tag form lets you write biome = "#minecraft:is_forest" instead of listing every individual forest biome separately.
whenMolang
Section titled “whenMolang”The full escape hatch for predicates that don’t fit any of the above. Set whenMolang to a Cobblemon Molang expression — anything the runtime can evaluate — and a non-zero result counts as a match.
match { species = "umbreon" whenMolang = """ q.world.is_time('night') && q.pokemon.level >= 50 && (q.pokemon.shiny || q.world.is_thundering) """}The runtime binds q.pokemon, q.player, q.world, and q.battle — the same surface Cobblemon’s NPC dialogue and Journey filters use. ShowdownActions also registers a few extra helpers under q.world.*. The full surface and helpers are documented at Molang Predicates.
whenMolang evaluates once at battle start for each (action, pokemon) candidate, after the property checks and before the final gates (permission, chance, mutexGroup). It is not re-evaluated on switch-in.
If whenMolang throws or fails to parse, ShowdownActions logs a warning and treats the result as false (no match). Actions never crash a battle.
Triple-quoted strings
Section titled “Triple-quoted strings”HOCON supports """...""" for multi-line strings without escaping. Use that for any non-trivial predicate — the result is much more readable than wrapping a long expression in single quotes.
How the Pieces Combine
Section titled “How the Pieces Combine”The match pipeline at battle start:
- For each action and each Pokémon on each player’s team:
- Property check runs first. If any populated property field doesn’t match, the action is rejected for this
(actor, pokemon)pair. No Molang is run. - Compile the sugar predicates and
whenMolanginto a single combined Molang expression. (The compilation result is logged at debug-trace level.) - Evaluate the combined Molang against the live battle context. A non-zero result is “match”.
- The first matching Pokémon on the actor’s team wins for this action — ShowdownActions doesn’t fire the same action multiple times per actor even if several team members would match.
After matching:
- Permission gate — if
permissionis set, the player must hold that node (looked up via fabric-permissions-api / LuckPerms). Missing nodes default to denied. - Chance gate — if
chance < 1.0, a uniform[0, 1)roll must come in below it. - Mutex resolution — within a
mutexGroup, only the highest-priority winner survives.
Then the action’s effects fire. Cobblemon-side effects (forced aspects, console commands) run immediately. Showdown-side effects are queued for the matched Pokémon’s slot and applied as a volatile when it switches in.
Examples
Section titled “Examples”A simple species + level gate:
match { species = "garchomp" minLevel = 50}A type filter combined with weather:
match { type = "fire" sugar { weather = "clear" }}Held-item check with a property fallthrough:
match { heldItem = "cobblemon:choice_band"}Tag-based biome gate (any forest variant):
match { sugar { biome = "#minecraft:is_forest" }}Pure Molang with no property fields:
match { whenMolang = "q.pokemon.hp_ratio < 0.25"}Mixing all three:
match { ability = "swiftswim" sugar { weather = "rain" } whenMolang = "q.pokemon.level >= 30 && q.pokemon.shiny"}Next Steps
Section titled “Next Steps”- Apply Clause — the other half of every action.
- Targeting — where the resulting effect lands.
- Molang Predicates — the full
whenMolangsurface.