Actions
An action list is ordered, and the first action that resolves successfully wins. "Resolves" means the runner found a matching, castable card and a valid target.
Shorthand actions
actions:
- pass
- flee
- draw
These are the only three bare scalars. pass always resolves, which makes it a terminator —
nothing after it in the phase, and no lower-priority phase, will run once it is reached.
Each also has a mapping form, which is the one to use when you want a guard on it:
actions:
- flee: true
when:
health_below: 15
- pass: true
The value must be true. pass: false does not disable the action — it decodes to an action
with nothing set, which never resolves and silently falls through.
- pss parses without error into an empty action that can never resolve. The file loads,
strategies validate passes, and the phase does nothing.
Only the mapping form is strictly validated — - cst: {} is a hard error. Until this is
fixed, treat scalar actions as unchecked and confirm behaviour in a real fight.
cast
The general-purpose spell selector.
- cast:
type: damage
school: storm
aoe: true
min_pips: 4
enchant: required
target: boss
| Field | Values |
|---|---|
type | damage, heal, buff, debuff, charm, ward, aura, enchant, other |
school | Any school, or any |
aoe | boolean |
name | Card name |
enchant | none, required |
enchants | Ordered enchant chain — see Enchants |
target | Any target selector |
min_pips / max_pips | Pip cost bounds, inclusive |
swap | charm/charms, ward/wards, ot/over_time |
Fields are ANDed. Leaving one out means "don't care".
cast is the only action whose school: accepts any; on every other action any is treated
as a literal school name and matches nothing.
Both pip bounds are ignored when set to 0, so max_pips: 0 does not mean "free spells
only"; it means "no upper bound". To match only zero-pip cards, filter another way.
Only cards the game currently reports as castable are considered at all, so a spell you cannot afford this round is skipped before any filter is applied.
enchant: required means the action fails if no matching enchant card is available, and
the runner moves to the next action. enchant: none casts the card plain. Omitting enchant
leaves the card alone — the only thing that can still enchant it is the round-start
enchant.auto pass, which touches damage spells only.
enchant: looks at the card, not the actionOn a cast: action the enchant policy is chosen from the spell type of the card that matched.
A cast: that picks up a blade is a charm, so it uses your enchant.support policy — not
enchant.blade, which only applies to the blade: action. If no policy resolves at all,
enchant: required fails the action every round. See
Enchants → which policy applies.
swap
Narrows the match to cards carrying a swap effect for one hanging category:
- cast:
swap: charm
target: boss
swap: aura is a load error, not a filter that never matches — there is no swap-aura
effect in the client. (Deimos accepts it and silently never matches.) An unknown category is
also a load error. Cards that swap everything carry a different effect and are not matched by
any category.
Naming a card
- cast: { name: 'Glowbug Squall', enchant: required }
Names are matched against the card's name and display name. Quote anything with spaces — YAML is forgiving until it is not.
Naming a specific card is the right move when you have one you actually want. Filters are for "whatever storm AOE I happen to have".
The support actions
blade, trap, shield, and heal are cast with a spell-type filter baked in. They pick
the first castable card matching:
| Action | Matches |
|---|---|
blade | Any charm |
trap | A ward whose target type is an enemy |
shield | A ward whose target type is self or an ally |
heal | Any heal |
That is a filter on what the card is, not on its name. blade: will happily pick a
weakness-style debuff charm if that is the first charm in hand — narrow it with school:, or
use cast: { name: ... } when you want a specific card.
Their school: matches the card's school by name — any of the eleven, including star, sun,
moon, and shadow. What it does not accept is any: unlike cast:, there is no wildcard
here, and school: any matches nothing at all. Omit school: instead.
blade
- blade:
school: storm
target: self
Fields: school, target, enchant.
Ally targeting works for support play:
- blade:
school: fire
target: p1
enchant: required
Combined with an enchant policy, this is how a support character puts sharpened blades on the hitter:
enchant:
blade:
prefer: sharpen
phases:
main:
actions:
- blade: { school: fire, target: p1, enchant: required }
trap
- trap:
school: storm
target: boss
Fields: school, name, target, enchant.
trap takes a name where blade does not, because named traps like Feint are common enough
to need it:
- trap: { name: Feint, target: boss, enchant: required }
shield
- shield:
school: ice
target: p1
Fields: school, target, enchant.
heal
- heal:
school: life
target: lowest_health_ally
Fields: target, school, enchant.
lowest_health_ally is nearly always the right target. self when you specifically mean
yourself.
wand_hit
- wand_hit:
target: first
Field: target.
A chip of damage from your equipment. Specifically: of the castable item cards in hand whose spell type is damage, it takes the one with the lowest pip cost. That is your wand attack in practice, but it is chosen by "item card" rather than by name, so a pet or jewel attack card can win instead.
It is not guaranteed to be free, and it does not resolve at all when no item damage card is in
hand — which makes it a good last line in an execute phase but a bad substitute for pass.
summon_minion
- summon_minion: {}
No fields. The empty mapping is required — YAML needs a value.
It takes the first castable card whose target type is "friendly minion", so it depends on having a minion spell in hand rather than on any configuration.
discard
- discard:
type: enchant
- discard:
school: moon
Fields: type, school.
Unlike every other action, discard does not check whether the card is castable — the
whole point is to throw away cards you cannot or will not play. That also means
- discard: {} with no filters matches the very first card in hand, which is almost never
what you want. Always give it a type: or a school:.
Discarding clears hand space for a better draw. Common on a hitter that keeps drawing utility cards it will never cast:
phases:
cleanup:
priority: 5
when:
all:
- pips_above: 5
- round_above: 3
actions:
- discard: { school: moon }
Low priority so it only runs when nothing better is available.
Per-action guards
Any action can carry a when: clause, evaluated before it tries to resolve a card.
actions:
- blade: { school: fire, target: p1 }
when:
no_blade: { target: p1, school: fire }
- cast: { type: damage, school: fire }
when:
has_blade: { target: p1, school: fire }
The when: is a sibling of the action key inside the same list item, not nested under it.
Actions with no when: always run.
Guard targets default to self in the scalar and boolean forms. To check someone else, use
the object form with an explicit target:. Full detail in
Phases → per-action guards.
One key per action
Each action object holds exactly one action key.
# wrong
- cast: { type: damage }
blade: { school: fire }
# right
- cast: { type: damage }
- blade: { school: fire }
The first form parses — both fields are known to the decoder — and the schema rejects it, so your editor will flag it, but the loader will not. At runtime exactly one wins, by a fixed internal order:
pass → flee → draw → cast → blade → trap → shield → heal → wand_hit → summon_minion → discard
So - cast: {...} plus blade: {...} in one item always runs the cast and silently drops the
blade. Deterministic, but not a feature.
Ordering
Most specific first:
actions:
# 1. the named card I actually want
- cast: { name: 'Glowbug Squall', enchant: required }
# 2. any enchanted storm AOE worth the pips
- cast: { type: damage, school: storm, aoe: true, min_pips: 4, enchant: required }
# 3. any storm damage at all
- cast: { type: damage, school: storm }
# 4. free chip damage
- wand_hit: { target: first }
Inverting that list gives you a strategy that wand-hits every round.