Introduction to ShowdownActions
Introduction to ShowdownActions
Section titled “Introduction to ShowdownActions”ShowdownActions is a Fabric mod for Cobblemon servers that lets you declare Showdown effects — stat boosts, status, weather, terrain, aspects, even raw Showdown JS handlers — in plain .conf files, gated by Molang predicates that fire at battle start.
The pitch is short: form changes that depend on time of day, ability buffs that depend on weather, species-specific quirks that should always trigger on switch-in, “this Pokémon is always Toxic on send-out” — all of it normally requires writing a custom Java/Kotlin mod for each case. ShowdownActions packages that plumbing once. You write the .conf, ShowdownActions wires up the Cobblemon battle event → Molang predicate → Showdown injection.
How It Works
Section titled “How It Works”Every action is one declarative file under config/showdown_actions/actions/. When a battle is about to start:
- Each registered action is checked against every Pokémon on each player’s team.
- Property checks happen first (species, ability, level bands, etc.). Misses are dropped immediately.
- Anything dynamic — time of day, weather, biome, custom predicates — is evaluated as Molang against the live
(player, pokemon, world, battle)context. - Surviving matches go through final gates: optional permission node, probability roll, and mutex-group resolution (so only the highest-priority winner fires when several variants compete).
- Cobblemon-side effects (forced aspects, console commands) run immediately. Showdown-side effects (boosts, status, weather, terrain, custom hooks) are applied as a Showdown volatile when the matched Pokémon switches in.
The whole pipeline is fault-tolerant. Missing fields are treated as “don’t care”, failed Molang evaluations log a warning and count as no match, and a hot reload via /showdownactions reload never restarts Showdown.
The Three Authoring Tiers
Section titled “The Three Authoring Tiers”ShowdownActions is designed so you only reach for power you actually need. The three tiers escalate in capability:
Tier 1 — Sugar (no scripting)
: match { species, form, type, ability, heldItem, aspect, minLevel, maxLevel, ... } paired with apply { aspect, boosts { atk, def, ... }, status, weather, terrain, message, command }. Covers the majority of “this species in these conditions gets these stat changes” cases without touching a script.
Tier 2 — Raw Molang predicates
: When sugar can’t express the gate, drop a whenMolang = "..." expression into the match clause. You get q.pokemon, q.player, q.world, and q.battle bindings — the same surface the rest of Cobblemon’s Molang ecosystem exposes — plus a few ShowdownActions-specific helpers under q.world.*. Compose these freely with the sugar fields; everything is ANDed.
Tier 3 — Raw Showdown JS
: When the effect itself can’t be reduced to “boost some stats / set a status / change the weather”, drop a showdown { conditionId, hooks { onStart, onModifyAtk, onResidual, ... } } block. The hooks are passed verbatim to Cobblemon’s Showdown JS context as a real Dex.data.Conditions[…] entry. Anything Showdown supports is supported here — if it’s a function key Showdown calls during battle simulation, you can hook it.
Use the lowest tier that does the job. Most actions never need to leave sugar; a handful need a Molang gate; a few percent need raw JS.
What This Looks Like
Section titled “What This Looks Like”A “Lycanroc gets +1 Spe/Atk and a dusk aspect at dusk” rule, in full:
id = "lycanroc_dusk"displayName = "Lycanroc - Dusk Form"target = "SELF"
match { species = "lycanroc" sugar { time = "dusk" }}
apply { aspect = "dusk" boosts { spe = 1, atk = 1 } message = "{name} resonates with the dusk light!"}A “Swift Swim mons gain +2 Speed in the rain, but only if they’re at least level 30”:
id = "rainy_swift_swim"match { ability = "swiftswim" whenMolang = "q.world.is_raining && q.pokemon.level >= 30"}apply { boosts { spe = 2 }}A “Gyarados gets a custom Intimidate handler that runs whatever Showdown code you want”:
id = "custom_intimidate"match { species = "gyarados" }
showdown { conditionId = "showdownactionsgyaradosintimidate" conditionName = "Sigil Intimidate" scope = "volatile" hooks { onStart = """function(pokemon) { var foe = pokemon.side.foe.active[0]; if (foe && !foe.fainted) { this.boost({atk: -2}, foe, pokemon); this.add('-message', pokemon.name + ' glares menacingly at ' + foe.name + '!'); } }""" }}What You Need
Section titled “What You Need”Required:
- Minecraft 1.21.1 (Fabric)
- Java 21+
- Cobblemon (compatible build)
- Ceremony 4.7+
- Fabric API
- Fabric Language Kotlin
Optional but recommended:
- fabric-permissions-api / LuckPerms (for the
permissionfield on actions)
If you’ve used Journey, the predicate model will feel familiar — ShowdownActions reuses Cobblemon’s Molang runtime the same way Journey’s filters do, with the same q.pokemon / q.player / q.world bindings. The big difference is when it runs: Journey predicates fire at task-event time; ShowdownActions predicates fire once at battle start, then the Showdown side takes over.
Where To Go Next
Section titled “Where To Go Next”- Installation — drop the jar in, where the configs live, what to expect on first launch.
- Configuration —
config.confsettings, hot reload, debug logging. - Action Files — the structure of a single
.conf, the three tiers in detail. - Match Clause and Apply Clause — the two halves of every action.
- Molang Predicates — bindings, helpers, what you can ask the world.
- Showdown Research — how the Showdown engine is structured and how to learn enough of it to write your own raw hooks.