enjine 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +28 -0
- data/LICENSE.txt +21 -0
- data/README.md +201 -0
- data/SUPPORTED.md +387 -0
- data/docs/ARCHITECTURE.md +141 -0
- data/docs/DESIGN.md +139 -0
- data/docs/MECHANICS_COVERAGE.md +670 -0
- data/docs/guides/adding-a-mechanic.md +60 -0
- data/docs/guides/api.md +614 -0
- data/docs/guides/card-data-format.md +1472 -0
- data/docs/guides/deck-builder.md +157 -0
- data/docs/plans/standard.md +371 -0
- data/docs/reference/abilities.md +1680 -0
- data/docs/reference/costs.md +339 -0
- data/docs/reference/counters.md +121 -0
- data/docs/reference/decisions.md +102 -0
- data/docs/reference/effects.md +2340 -0
- data/docs/reference/filters.md +172 -0
- data/docs/reference/keywords.md +647 -0
- data/docs/reference/subtypes.md +169 -0
- data/docs/reference/supertypes.md +31 -0
- data/docs/reference/triggers.md +797 -0
- data/docs/reference/types.md +216 -0
- data/exe/enjine +7 -0
- data/lib/enjine/actions/activate_mana_ability.rb +24 -0
- data/lib/enjine/actions/cast.rb +57 -0
- data/lib/enjine/actions/pass.rb +18 -0
- data/lib/enjine/actions/play_land.rb +52 -0
- data/lib/enjine/actions/special_action.rb +37 -0
- data/lib/enjine/actions.rb +20 -0
- data/lib/enjine/agents/busy.rb +27 -0
- data/lib/enjine/agents/random.rb +28 -0
- data/lib/enjine/card_definition.rb +433 -0
- data/lib/enjine/card_faces.rb +116 -0
- data/lib/enjine/catalog.rb +56 -0
- data/lib/enjine/characteristics.rb +39 -0
- data/lib/enjine/cli/text.rb +295 -0
- data/lib/enjine/cli.rb +312 -0
- data/lib/enjine/decisions/bottom.rb +29 -0
- data/lib/enjine/decisions/casting.rb +188 -0
- data/lib/enjine/decisions/combat.rb +75 -0
- data/lib/enjine/decisions/discard.rb +29 -0
- data/lib/enjine/decisions/effect_choice.rb +21 -0
- data/lib/enjine/decisions/enter_choice.rb +26 -0
- data/lib/enjine/decisions/legend_rule.rb +21 -0
- data/lib/enjine/decisions/mulligan.rb +26 -0
- data/lib/enjine/decisions/order_triggers.rb +21 -0
- data/lib/enjine/decisions/play_or_draw.rb +19 -0
- data/lib/enjine/decisions/priority.rb +22 -0
- data/lib/enjine/decisions/untap_choice.rb +35 -0
- data/lib/enjine/decisions.rb +22 -0
- data/lib/enjine/deck.rb +151 -0
- data/lib/enjine/deck_builder.rb +454 -0
- data/lib/enjine/docs.rb +90 -0
- data/lib/enjine/engine/ability_layers.rb +118 -0
- data/lib/enjine/engine/attachments.rb +86 -0
- data/lib/enjine/engine/casting/alt_casting.rb +254 -0
- data/lib/enjine/engine/casting/cast_choices.rb +292 -0
- data/lib/enjine/engine/casting/casting.rb +326 -0
- data/lib/enjine/engine/casting/cost_options.rb +99 -0
- data/lib/enjine/engine/casting/costs.rb +117 -0
- data/lib/enjine/engine/casting/payment.rb +353 -0
- data/lib/enjine/engine/casting/permissions.rb +412 -0
- data/lib/enjine/engine/casting/target_counts.rb +113 -0
- data/lib/enjine/engine/casting/waterbend.rb +90 -0
- data/lib/enjine/engine/characteristics.rb +379 -0
- data/lib/enjine/engine/class_levels.rb +25 -0
- data/lib/enjine/engine/combat.rb +315 -0
- data/lib/enjine/engine/copies.rb +208 -0
- data/lib/enjine/engine/damage.rb +196 -0
- data/lib/enjine/engine/designations.rb +46 -0
- data/lib/enjine/engine/emblems.rb +37 -0
- data/lib/enjine/engine/entering.rb +156 -0
- data/lib/enjine/engine/face_down.rb +30 -0
- data/lib/enjine/engine/faces.rb +222 -0
- data/lib/enjine/engine/follow_ups.rb +27 -0
- data/lib/enjine/engine/mana_sources.rb +230 -0
- data/lib/enjine/engine/phasing.rb +74 -0
- data/lib/enjine/engine/pregame.rb +95 -0
- data/lib/enjine/engine/priority.rb +47 -0
- data/lib/enjine/engine/resolution.rb +168 -0
- data/lib/enjine/engine/rules.rb +287 -0
- data/lib/enjine/engine/sagas.rb +41 -0
- data/lib/enjine/engine/speed.rb +104 -0
- data/lib/enjine/engine/spell_grants.rb +46 -0
- data/lib/enjine/engine/state_based_actions.rb +157 -0
- data/lib/enjine/engine/triggers.rb +364 -0
- data/lib/enjine/engine/turn_history.rb +240 -0
- data/lib/enjine/engine/turn_structure.rb +224 -0
- data/lib/enjine/engine/zones.rb +278 -0
- data/lib/enjine/engine.rb +55 -0
- data/lib/enjine/errors.rb +62 -0
- data/lib/enjine/events.rb +161 -0
- data/lib/enjine/frames.rb +84 -0
- data/lib/enjine/fuzz.rb +112 -0
- data/lib/enjine/game.rb +204 -0
- data/lib/enjine/history.rb +26 -0
- data/lib/enjine/mana.rb +142 -0
- data/lib/enjine/mechanics/abilities/additional_triggers.rb +86 -0
- data/lib/enjine/mechanics/abilities/chosen_values.rb +213 -0
- data/lib/enjine/mechanics/abilities/cost_changes.rb +154 -0
- data/lib/enjine/mechanics/abilities/damage_replacements.rb +569 -0
- data/lib/enjine/mechanics/abilities/double_counters.rb +71 -0
- data/lib/enjine/mechanics/abilities/entering_choices.rb +91 -0
- data/lib/enjine/mechanics/abilities/entering_copy.rb +69 -0
- data/lib/enjine/mechanics/abilities/enters_tapped.rb +159 -0
- data/lib/enjine/mechanics/abilities/enters_with_counters.rb +116 -0
- data/lib/enjine/mechanics/abilities/mana_ability.rb +209 -0
- data/lib/enjine/mechanics/abilities/play_permissions.rb +527 -0
- data/lib/enjine/mechanics/abilities/player_rules.rb +169 -0
- data/lib/enjine/mechanics/abilities/replacement_effects.rb +368 -0
- data/lib/enjine/mechanics/abilities/restrictions.rb +144 -0
- data/lib/enjine/mechanics/abilities/spell_keywords.rb +55 -0
- data/lib/enjine/mechanics/abilities/stack_abilities.rb +542 -0
- data/lib/enjine/mechanics/abilities/static.rb +365 -0
- data/lib/enjine/mechanics/abilities/tapping_power.rb +71 -0
- data/lib/enjine/mechanics/abilities/turn_rules.rb +49 -0
- data/lib/enjine/mechanics/card_spec.rb +177 -0
- data/lib/enjine/mechanics/copy_except.rb +73 -0
- data/lib/enjine/mechanics/costs/base.rb +115 -0
- data/lib/enjine/mechanics/costs/behold.rb +76 -0
- data/lib/enjine/mechanics/costs/discard.rb +65 -0
- data/lib/enjine/mechanics/costs/exert.rb +29 -0
- data/lib/enjine/mechanics/costs/exile_cards.rb +39 -0
- data/lib/enjine/mechanics/costs/from_graveyard.rb +45 -0
- data/lib/enjine/mechanics/costs/get_counters.rb +39 -0
- data/lib/enjine/mechanics/costs/mana.rb +37 -0
- data/lib/enjine/mechanics/costs/one_of.rb +49 -0
- data/lib/enjine/mechanics/costs/pay_life.rb +50 -0
- data/lib/enjine/mechanics/costs/remove_counters.rb +51 -0
- data/lib/enjine/mechanics/costs/sacrifice.rb +59 -0
- data/lib/enjine/mechanics/costs/tap.rb +56 -0
- data/lib/enjine/mechanics/costs/waterbend.rb +29 -0
- data/lib/enjine/mechanics/counters/kinds.rb +190 -0
- data/lib/enjine/mechanics/effects/add_subtypes.rb +30 -0
- data/lib/enjine/mechanics/effects/airbend.rb +41 -0
- data/lib/enjine/mechanics/effects/amass.rb +124 -0
- data/lib/enjine/mechanics/effects/assimilate.rb +72 -0
- data/lib/enjine/mechanics/effects/base.rb +148 -0
- data/lib/enjine/mechanics/effects/become.rb +182 -0
- data/lib/enjine/mechanics/effects/behold.rb +48 -0
- data/lib/enjine/mechanics/effects/blight.rb +94 -0
- data/lib/enjine/mechanics/effects/choose_cards.rb +168 -0
- data/lib/enjine/mechanics/effects/copy.rb +370 -0
- data/lib/enjine/mechanics/effects/counter_spell.rb +21 -0
- data/lib/enjine/mechanics/effects/counters.rb +161 -0
- data/lib/enjine/mechanics/effects/damage.rb +72 -0
- data/lib/enjine/mechanics/effects/delayed_triggers.rb +112 -0
- data/lib/enjine/mechanics/effects/destroy.rb +22 -0
- data/lib/enjine/mechanics/effects/discard.rb +86 -0
- data/lib/enjine/mechanics/effects/draw.rb +16 -0
- data/lib/enjine/mechanics/effects/earthbend.rb +70 -0
- data/lib/enjine/mechanics/effects/emblem.rb +52 -0
- data/lib/enjine/mechanics/effects/endure.rb +63 -0
- data/lib/enjine/mechanics/effects/exile.rb +49 -0
- data/lib/enjine/mechanics/effects/extra_turn.rb +122 -0
- data/lib/enjine/mechanics/effects/faces.rb +115 -0
- data/lib/enjine/mechanics/effects/fight.rb +76 -0
- data/lib/enjine/mechanics/effects/flip_coins.rb +97 -0
- data/lib/enjine/mechanics/effects/gain_control.rb +94 -0
- data/lib/enjine/mechanics/effects/game_end.rb +42 -0
- data/lib/enjine/mechanics/effects/library.rb +327 -0
- data/lib/enjine/mechanics/effects/life.rb +83 -0
- data/lib/enjine/mechanics/effects/look_at_hand.rb +17 -0
- data/lib/enjine/mechanics/effects/loot.rb +118 -0
- data/lib/enjine/mechanics/effects/mana.rb +96 -0
- data/lib/enjine/mechanics/effects/may.rb +82 -0
- data/lib/enjine/mechanics/effects/meld.rb +77 -0
- data/lib/enjine/mechanics/effects/mill.rb +22 -0
- data/lib/enjine/mechanics/effects/pay.rb +231 -0
- data/lib/enjine/mechanics/effects/phasing.rb +51 -0
- data/lib/enjine/mechanics/effects/proliferate.rb +72 -0
- data/lib/enjine/mechanics/effects/pump.rb +60 -0
- data/lib/enjine/mechanics/effects/put_in_library.rb +98 -0
- data/lib/enjine/mechanics/effects/return_choice.rb +29 -0
- data/lib/enjine/mechanics/effects/return_to_battlefield.rb +80 -0
- data/lib/enjine/mechanics/effects/return_to_hand.rb +20 -0
- data/lib/enjine/mechanics/effects/roll_dice.rb +45 -0
- data/lib/enjine/mechanics/effects/sacrifice.rb +118 -0
- data/lib/enjine/mechanics/effects/scry_and_surveil.rb +74 -0
- data/lib/enjine/mechanics/effects/search.rb +156 -0
- data/lib/enjine/mechanics/effects/tap_untap.rb +23 -0
- data/lib/enjine/mechanics/effects/tokens.rb +76 -0
- data/lib/enjine/mechanics/filters/any_target.rb +15 -0
- data/lib/enjine/mechanics/filters/base.rb +386 -0
- data/lib/enjine/mechanics/filters/graveyard.rb +23 -0
- data/lib/enjine/mechanics/filters/one_of.rb +55 -0
- data/lib/enjine/mechanics/filters/permanents.rb +52 -0
- data/lib/enjine/mechanics/filters/players.rb +20 -0
- data/lib/enjine/mechanics/filters/stack.rb +97 -0
- data/lib/enjine/mechanics/follow_up.rb +46 -0
- data/lib/enjine/mechanics/keywords/alternative_costs.rb +361 -0
- data/lib/enjine/mechanics/keywords/beam_me_up.rb +78 -0
- data/lib/enjine/mechanics/keywords/changeling.rb +58 -0
- data/lib/enjine/mechanics/keywords/convoke.rb +77 -0
- data/lib/enjine/mechanics/keywords/crew.rb +155 -0
- data/lib/enjine/mechanics/keywords/cycling.rb +58 -0
- data/lib/enjine/mechanics/keywords/decayed.rb +25 -0
- data/lib/enjine/mechanics/keywords/devour.rb +61 -0
- data/lib/enjine/mechanics/keywords/evergreen.rb +54 -0
- data/lib/enjine/mechanics/keywords/extort.rb +26 -0
- data/lib/enjine/mechanics/keywords/flags.rb +46 -0
- data/lib/enjine/mechanics/keywords/foretell.rb +45 -0
- data/lib/enjine/mechanics/keywords/gift.rb +50 -0
- data/lib/enjine/mechanics/keywords/hideaway.rb +28 -0
- data/lib/enjine/mechanics/keywords/increment.rb +31 -0
- data/lib/enjine/mechanics/keywords/job_select.rb +29 -0
- data/lib/enjine/mechanics/keywords/kicker.rb +92 -0
- data/lib/enjine/mechanics/keywords/mobilize_and_firebending.rb +84 -0
- data/lib/enjine/mechanics/keywords/paradigm.rb +91 -0
- data/lib/enjine/mechanics/keywords/power_up.rb +48 -0
- data/lib/enjine/mechanics/keywords/protection.rb +111 -0
- data/lib/enjine/mechanics/keywords/prowess.rb +30 -0
- data/lib/enjine/mechanics/keywords/riot.rb +35 -0
- data/lib/enjine/mechanics/keywords/saddle.rb +115 -0
- data/lib/enjine/mechanics/keywords/speed.rb +89 -0
- data/lib/enjine/mechanics/keywords/station.rb +163 -0
- data/lib/enjine/mechanics/keywords/storied.rb +41 -0
- data/lib/enjine/mechanics/keywords/storm_and_cascade.rb +86 -0
- data/lib/enjine/mechanics/keywords/sunburst.rb +29 -0
- data/lib/enjine/mechanics/keywords/suspend.rb +191 -0
- data/lib/enjine/mechanics/keywords/teamwork.rb +45 -0
- data/lib/enjine/mechanics/keywords/ward.rb +67 -0
- data/lib/enjine/mechanics/linked.rb +67 -0
- data/lib/enjine/mechanics/mana_spent.rb +99 -0
- data/lib/enjine/mechanics/selector.rb +207 -0
- data/lib/enjine/mechanics/subtypes/auras_and_equipment.rb +176 -0
- data/lib/enjine/mechanics/subtypes/basic_land_types.rb +36 -0
- data/lib/enjine/mechanics/subtypes/class_levels.rb +102 -0
- data/lib/enjine/mechanics/subtypes/saga.rb +89 -0
- data/lib/enjine/mechanics/supertypes/basic.rb +17 -0
- data/lib/enjine/mechanics/supertypes/legendary.rb +18 -0
- data/lib/enjine/mechanics/triggers/ability_triggers.rb +51 -0
- data/lib/enjine/mechanics/triggers/any_of.rb +43 -0
- data/lib/enjine/mechanics/triggers/base.rb +228 -0
- data/lib/enjine/mechanics/triggers/casting.rb +219 -0
- data/lib/enjine/mechanics/triggers/combat.rb +123 -0
- data/lib/enjine/mechanics/triggers/damage.rb +130 -0
- data/lib/enjine/mechanics/triggers/dilemma.rb +37 -0
- data/lib/enjine/mechanics/triggers/draw_and_discard.rb +85 -0
- data/lib/enjine/mechanics/triggers/keyword_actions.rb +55 -0
- data/lib/enjine/mechanics/triggers/life.rb +35 -0
- data/lib/enjine/mechanics/triggers/steps.rb +39 -0
- data/lib/enjine/mechanics/triggers/tap_and_counters.rb +111 -0
- data/lib/enjine/mechanics/triggers/zone_changes.rb +249 -0
- data/lib/enjine/mechanics/types/kindred.rb +30 -0
- data/lib/enjine/mechanics/types/land.rb +24 -0
- data/lib/enjine/mechanics/types/permanent_types.rb +67 -0
- data/lib/enjine/mechanics/types/spell_types.rb +38 -0
- data/lib/enjine/mechanics/values.rb +675 -0
- data/lib/enjine/mechanics.rb +74 -0
- data/lib/enjine/player.rb +30 -0
- data/lib/enjine/primitive.rb +61 -0
- data/lib/enjine/replacements.rb +191 -0
- data/lib/enjine/rng.rb +57 -0
- data/lib/enjine/rules_monitor.rb +404 -0
- data/lib/enjine/setup.rb +45 -0
- data/lib/enjine/snapshot/player_checks.rb +86 -0
- data/lib/enjine/snapshot/turn_checks.rb +91 -0
- data/lib/enjine/snapshot.rb +307 -0
- data/lib/enjine/state.rb +173 -0
- data/lib/enjine/steps.rb +117 -0
- data/lib/enjine/tokens.rb +25 -0
- data/lib/enjine/verify.rb +233 -0
- data/lib/enjine/version.rb +7 -0
- data/lib/enjine/view.rb +162 -0
- data/lib/enjine.rb +46 -0
- metadata +315 -0
|
@@ -0,0 +1,1472 @@
|
|
|
1
|
+
# The card DSL — writing cards for Enjine
|
|
2
|
+
|
|
3
|
+
Enjine has no built-in card list and never reads card text. A card exists only as a
|
|
4
|
+
**data block** (JSON) that you write by hand in Enjine's card DSL. Enjine validates
|
|
5
|
+
it and compiles it. Nothing parses rules text: what a card does is exactly what its
|
|
6
|
+
data block says.
|
|
7
|
+
|
|
8
|
+
```json
|
|
9
|
+
{ "name": "Ember Sprite", "mana_cost": "{1}{R}", "types": ["creature"], "subtypes": ["elemental"],
|
|
10
|
+
"keywords": ["haste"], "power": 2, "toughness": 1,
|
|
11
|
+
"abilities": [ { "triggered": { "when": { "enters": {} },
|
|
12
|
+
"effects": [ { "deal_damage": { "amount": 1, "to": { "target": 0 } } } ],
|
|
13
|
+
"targets": ["any_target"] } } ] }
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
If a block uses something Enjine doesn't support, or has a typo, validation
|
|
17
|
+
**fails with a precise error** that names the exact path in your data
|
|
18
|
+
(`abilities[0].triggered.effects[0].deal_damage.amount: …`). Nothing is ever
|
|
19
|
+
guessed or silently dropped.
|
|
20
|
+
|
|
21
|
+
**Source of truth for what exists:** [`SUPPORTED.md`](../../SUPPORTED.md) and
|
|
22
|
+
[`docs/reference/`](../reference). Both are generated from the code. A primitive
|
|
23
|
+
listed there works, and one that isn't listed doesn't exist yet.
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## 1. Adding a card
|
|
28
|
+
|
|
29
|
+
1. Write the data block using sections 2–5 (fields, mana abilities, abilities, examples).
|
|
30
|
+
2. Validate it: `bundle exec exe/enjine --validate cards.json` (section 7).
|
|
31
|
+
3. Put it where you need it:
|
|
32
|
+
- **In a deck document's `cards`** (section 6). This is the simplest option and needs nothing else.
|
|
33
|
+
- **In a card pool** for the [deck builder](deck-builder.md): a JSON array of cards
|
|
34
|
+
(`--pool cards.json`), such as a set or the cards you own.
|
|
35
|
+
- **In your own database** (section 8).
|
|
36
|
+
|
|
37
|
+
Tips:
|
|
38
|
+
- **Store the canonical form.** `CardDefinition#to_h` (the `card` in the validation
|
|
39
|
+
report) is normalized and round-trips exactly.
|
|
40
|
+
- **Record the printing, not a vendor id.** The set code and collector number printed on
|
|
41
|
+
the card (`trk`, `12`) can be looked up in any card source.
|
|
42
|
+
- **Multi-face cards** (split, adventure, MDFC, transform) aren't modelled yet.
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## 2. Top-level fields
|
|
47
|
+
|
|
48
|
+
| Field | Required | Type | Notes |
|
|
49
|
+
| --- | --- | --- | --- |
|
|
50
|
+
| `name` | yes | string | Unique (a card with faces: see 4.17). Its slug becomes the card id: `"Jace, the Mind Sculptor"` → `jace_the_mind_sculptor`. Two different definitions with the same name in one game raise `CatalogConflict`. |
|
|
51
|
+
| `types` | yes | list | Card types, lowercase: `land`, `creature`, `artifact`, `enchantment`, `planeswalker`, `instant`, `sorcery`. A card can have several (`["artifact", "creature"]`). |
|
|
52
|
+
| `mana_cost` | castable cards | string | `"{2}{W}{W}"`. Symbols: `{N}` generic, `{W}{U}{B}{R}{G}`, `{C}`, `{X}` (announced while casting; use the amount `"x"` in effects, see 4.7). Lands must **not** have one. A card without one can't be cast. Two-color hybrid `{R/G}` is supported (paid with either color, CR 107.4e), and so is monocolored hybrid `{2/B}` (one black mana or two mana of any type; mana value 2, CR 202.3f); Phyrexian and `{S}` are not yet. |
|
|
53
|
+
| `supertypes` | no | list | `basic` (exempt from the 4-copy rule). `legendary` and `snow`: not yet. |
|
|
54
|
+
| `subtypes` | no | list | Lowercase, spaces become `_`: `["human", "wizard"]`. Free-form labels, except the basic land types `plains island swamp mountain forest`, which grant their mana ability automatically (CR 305.6) and are valid only on lands. |
|
|
55
|
+
| `keywords` | no | list | Keyword abilities, lowercase: see `SUPPORTED.md` → *Keyword abilities*. |
|
|
56
|
+
| `power`, `toughness` | creatures | integer or amount | Required for creatures; not allowed on other types. A `*` defined by the card ("power is equal to the number of …", CR 604.3) is an amount, see 4.10. |
|
|
57
|
+
| `colors` | no | list | `W U B R G`. Only for a color indicator or a token's colors (CR 202.2e, 111.3); otherwise colors come from the mana cost. |
|
|
58
|
+
| `loyalty` | planeswalkers | integer | Starting loyalty. |
|
|
59
|
+
| `abilities` | no | list | Section 4. |
|
|
60
|
+
| `rules_text` | no | string | Informational only (shown in the CLI). Never interpreted. |
|
|
61
|
+
| `deck_limit` | no | `"any"` or integer | CR 100.2a exception: "A deck can have any number of cards named ~" is `"deck_limit": "any"`; "up to seven cards named ~" is `7`. Without it a deck may hold four (basic lands: any number). |
|
|
62
|
+
| `rarity` | no | string | Catalogue data, e.g. `"rare"`. The rules never read it. |
|
|
63
|
+
| `printing` | no | object | `{ "set": "trk", "number": "12" }`: the set code and collector number printed on the card (a number may be given as an integer; it is stored as a string, since numbers like `"12a"` exist). The rules never read it, and two cards differing only in `rarity`/`printing` are the same card (a reprint). On a card with faces it sits beside `faces`. |
|
|
64
|
+
|
|
65
|
+
Any other field is an error. That's deliberate: a typo must never be silently ignored.
|
|
66
|
+
|
|
67
|
+
Keyword *actions* such as scry and mill are effects inside an ability, not `keywords`.
|
|
68
|
+
Only keyword *abilities* (rule 702) go in `keywords`.
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## 3. Mana abilities of lands
|
|
73
|
+
|
|
74
|
+
Basic land types give their ability automatically, so don't write it:
|
|
75
|
+
|
|
76
|
+
```json
|
|
77
|
+
{ "name": "Forest", "types": ["land"], "supertypes": ["basic"], "subtypes": ["forest"] }
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Any other mana ability is written explicitly:
|
|
81
|
+
|
|
82
|
+
```json
|
|
83
|
+
{ "name": "Wastes", "types": ["land"], "supertypes": ["basic"],
|
|
84
|
+
"abilities": [{ "mana": { "cost": ["tap"], "produce": "{C}" } }] }
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
`produce` is a string of mana symbols: `"{G}"`, `"{C}{C}"`. When the colors are chosen,
|
|
88
|
+
use `color` instead of `produce`, the same shape as the `add_mana` effect (CR 605.3b:
|
|
89
|
+
the choice is made as the ability is activated, so each activation option names the
|
|
90
|
+
mana it adds, e.g. `{"type": "activate_mana", ..., "mana": "{G}{G}"}`). A mana cost
|
|
91
|
+
in a mana ability's `cost` ("{1}, {T}: Add …") is paid from the mana already in your
|
|
92
|
+
pool, so tap other sources first:
|
|
93
|
+
|
|
94
|
+
| Oracle text | Data |
|
|
95
|
+
| --- | --- |
|
|
96
|
+
| "{T}: Add one mana of any color." | `{ "mana": { "cost": ["tap"], "color": "any_one" } }` |
|
|
97
|
+
| "{T}: Add {R} or {G}." | `{ "mana": { "cost": ["tap"], "color": "any_one", "colors": ["R", "G"] } }` |
|
|
98
|
+
| "{T}: Add two mana in any combination of {R} and/or {G}." | `{ "mana": { "cost": ["tap"], "color": "any", "amount": 2, "colors": ["R", "G"] } }` |
|
|
99
|
+
| "{T}: Add {G} for each creature you control." | `{ "mana": { "cost": ["tap"], "produce": "{G}", "amount": { "count": { "each": "creature", "controller": "you" } } } }` |
|
|
100
|
+
| "{T}: Add X mana of any one color, where X is …" | `"color": "any_one", "amount": <amount>` |
|
|
101
|
+
| "As this land enters, choose a color. {T}: Add one mana of the chosen color." | `{ "choose_color": {} }`, `{ "mana": { "cost": ["tap"], "color": "chosen" } }` (also on `add_mana`, CR 607.2d) |
|
|
102
|
+
| "{T}, Pay 1 life: Add one mana of any color." | `{ "mana": { "cost": ["tap", { "pay_life": 1 }], "color": "any_one" } }` |
|
|
103
|
+
| "{T}: Add {C}. Spend this mana only to cast artifact spells or activate abilities of artifacts." | `{ "mana": { "cost": ["tap"], "produce": "{C}", "spend_only": { "spells": { "types": ["artifact"] }, "abilities": { "types": ["artifact"] } } } }` |
|
|
104
|
+
|
|
105
|
+
```json
|
|
106
|
+
{ "name": "Example Lyceum", "types": ["land"],
|
|
107
|
+
"abilities": [{ "mana": { "cost": ["tap", { "mana": "{5}" }], "color": "any_one",
|
|
108
|
+
"amount": { "distinct_types": { "each": "creature", "controller": "you" } } } }] }
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
**Restricted mana** (CR 106.6): `spend_only` takes `spells` and/or `abilities`, each a
|
|
112
|
+
card spec (`"card"` for any spell or source). The mana stays in its own part of the pool
|
|
113
|
+
(`restricted_mana` in the view) and is offered as `{"type": "pay_mana", "mana": "C",
|
|
114
|
+
"restricted": 0}` only while casting a matching spell or activating an ability of a
|
|
115
|
+
matching source; it can't pay a cost asked for while something resolves. `add_mana`
|
|
116
|
+
takes `spend_only` too ("Add {C}{C}. Spend this mana only to cast spells.").
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
## 4. Abilities
|
|
121
|
+
|
|
122
|
+
`abilities` is a list. Each entry is an object with **one key**: the ability kind.
|
|
123
|
+
|
|
124
|
+
| Kind | Where | Oracle text shape |
|
|
125
|
+
| --- | --- | --- |
|
|
126
|
+
| `spell` | instants, sorceries | the whole text of the spell |
|
|
127
|
+
| `activated` | permanents | `"[Cost]: [Effect]."` |
|
|
128
|
+
| `loyalty` | planeswalkers | `"+1: …"`, `"−2: …"` |
|
|
129
|
+
| `mana` | permanents | `"{T}: Add {G}."` |
|
|
130
|
+
|
|
131
|
+
### 4.1 Spell ability (instants and sorceries)
|
|
132
|
+
|
|
133
|
+
```json
|
|
134
|
+
{ "spell": {
|
|
135
|
+
"targets": ["any_target"],
|
|
136
|
+
"effects": [{ "deal_damage": { "amount": 3, "to": { "target": 0 } } }] } }
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
- `targets`: one target filter per "target" word in the oracle text, **in order of
|
|
140
|
+
appearance**. The text "deals 2 damage to target creature and 2 damage to target
|
|
141
|
+
player" has two targets: `["creature", "player"]`.
|
|
142
|
+
- `effects`: in the order the oracle text lists them. They resolve in that order (CR 608.2c).
|
|
143
|
+
- A spell with targets doesn't resolve if *all* its targets are illegal on
|
|
144
|
+
resolution. If some are illegal, only those are skipped.
|
|
145
|
+
|
|
146
|
+
### 4.2 Activated ability
|
|
147
|
+
|
|
148
|
+
```json
|
|
149
|
+
{ "activated": {
|
|
150
|
+
"cost": ["tap", { "mana": "{1}{R}" }],
|
|
151
|
+
"targets": ["any_target"],
|
|
152
|
+
"effects": [{ "deal_damage": { "amount": 1, "to": { "target": 0 } } }],
|
|
153
|
+
"timing": "instant" } }
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
- `cost`: costs before the colon. `"tap"` is `{T}`; `{ "mana": "{1}{R}" }` is a mana
|
|
157
|
+
cost; `"sacrifice"` sacrifices this permanent; `{ "sacrifice_permanent": { "filter": "creature", "other": true } }`,
|
|
158
|
+
`{ "tap_permanents": { "count": 3, "filter": ... } }` and `{ "remove_counters": { "count": 3 } }` are chosen while
|
|
159
|
+
activating; `"exile_self_from_graveyard"` / `"bottom_self_from_graveyard"` make it a graveyard ability. `{Q}` is
|
|
160
|
+
not supported yet. More costs (each also usable in a spell's `additional_cost`):
|
|
161
|
+
|
|
162
|
+
| Oracle words | Cost |
|
|
163
|
+
| --- | --- |
|
|
164
|
+
| "Discard a card" / "Discard two cards" / "Discard a land card" | `"discard"` / `{ "discard": { "count": 2 } }` / `{ "discard": { "card": { "types": ["land"] } } }` |
|
|
165
|
+
| "Discard a card at random" | `{ "discard": { "random": true } }` (CR 701.9b) |
|
|
166
|
+
| "Exile two cards from your graveyard" / "Exile a creature card from your graveyard" | `{ "exile_from_graveyard": { "count": 2 } }` / `{ "exile_from_graveyard": { "card": { "types": ["creature"] } } }` |
|
|
167
|
+
| "Pay 2 life" / "Pay X life" | `{ "pay_life": 2 }` / `{ "pay_life": "X" }` (X announced, at most your life; CR 119.4) |
|
|
168
|
+
| "Behold a Goblin" / "behold a Kithkin and exile it" | `{ "behold": { "card": { "subtypes": ["goblin"] } } }` / add `"exile": true` (the card is then `exiled_with` it, also after it leaves; CR 701.4) |
|
|
169
|
+
|
|
170
|
+
```json
|
|
171
|
+
{ "name": "Example Banner Champion", "mana_cost": "{2}{W}", "types": ["creature"], "subtypes": ["kithkin"],
|
|
172
|
+
"power": 4, "toughness": 4,
|
|
173
|
+
"abilities": [
|
|
174
|
+
{ "additional_cost": { "cost": [{ "behold": { "card": { "subtypes": ["kithkin"] }, "exile": true } }] } },
|
|
175
|
+
{ "triggered": { "when": { "leaves_battlefield": {} },
|
|
176
|
+
"effects": [{ "return_to_hand": { "to": "exiled_with" } }] } }] }
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
"You may behold a Dragon. If you do, …" is the effect `behold` under `may`:
|
|
180
|
+
`{ "may": { "effects": [{ "behold": { "card": { "subtypes": ["dragon"] } } }], "then": [...] } }`.
|
|
181
|
+
- `limit: "once_each_turn"`, `limit: "once"`, `only_if: condition`, `timing: "your_turn"`: "Activate only once each
|
|
182
|
+
turn / only once / only if … / only during your turn" (CR 602.5b; "only once" lasts as long as the object).
|
|
183
|
+
- `timing`: `"sorcery"` if the text says "Activate only as a sorcery"; otherwise omit.
|
|
184
|
+
|
|
185
|
+
Spells: `{ "kicker": { "cost": [...] } }` (condition `{ "kicked": true }`, spell `kicked_targets`) and
|
|
186
|
+
`{ "additional_cost": { "cost": [...] } }`. Targets: `{ "filter": f, "optional": true }` is "up to one target",
|
|
187
|
+
`"distinct_from": [0]` keeps a second target of the same word different; `"divide": N` with the effect
|
|
188
|
+
`distribute_counters` divides counters among them. "Counter … unless its controller pays {N}" is
|
|
189
|
+
`counter_unless_pays`; "you may pay {1}. If you do, …" is `pay`; ward is `{ "ward": { "cost": "{2}" } }`.
|
|
190
|
+
|
|
191
|
+
**A variable number of targets** (CR 601.2c, 115.3) is one target spec with a `count`: an amount
|
|
192
|
+
(`"x"`, a number, or a computed amount) or `"any"`. With `"optional": true` the count is a maximum
|
|
193
|
+
("up to"). The player announces how many (decision `choose_target_count`, skipped when the number
|
|
194
|
+
is fixed) and then chooses that many different objects; `{ "target": n }` then means each of them.
|
|
195
|
+
|
|
196
|
+
| Oracle words | Target spec |
|
|
197
|
+
| --- | --- |
|
|
198
|
+
| "X target creatures" | `{ "filter": "creature", "count": "x" }` |
|
|
199
|
+
| "two target creatures" | `{ "filter": "creature", "count": 2 }` |
|
|
200
|
+
| "up to X target creatures you control, where X is the number of lands you control" | `{ "filter": { "creature": { "controller": "you" } }, "count": { "count": { "each": "land", "controller": "you" } }, "optional": true }` |
|
|
201
|
+
| "any number of target players" | `{ "filter": "player", "count": "any" }` |
|
|
202
|
+
| "divided as you choose among any number of target creatures" | `{ "filter": "creature", "count": "any" }` with `"divide": N` (at most N targets; each gets at least one) |
|
|
203
|
+
|
|
204
|
+
```json
|
|
205
|
+
{ "name": "Example Mimic Raid", "mana_cost": "{X}{U}{B}", "types": ["sorcery"],
|
|
206
|
+
"abilities": [{ "spell": {
|
|
207
|
+
"targets": [{ "filter": { "creature": { "controller": "opponent" } }, "count": "x" }],
|
|
208
|
+
"effects": [
|
|
209
|
+
{ "create_token_copy": { "of": { "target": 0 }, "except": { "keywords": ["changeling"] } } },
|
|
210
|
+
{ "grant": { "keywords": ["haste"], "to": { "result": 0 } } },
|
|
211
|
+
{ "delayed_trigger": { "when": { "step_begins": { "step": "end", "whose": "each" } },
|
|
212
|
+
"effects": [{ "exile": { "to": { "result": 0 } } }] } }] } }] }
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### 4.3 Loyalty ability
|
|
216
|
+
|
|
217
|
+
```json
|
|
218
|
+
{ "loyalty": { "cost": -3, "targets": ["creature"],
|
|
219
|
+
"effects": [{ "deal_damage": { "amount": 3, "to": { "target": 0 } } }] } }
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
`cost` is the signed number: `+1` → `1`, `−2` → `-2`, `0` → `0`. Sorcery timing and
|
|
223
|
+
once-per-turn are automatic.
|
|
224
|
+
|
|
225
|
+
### 4.4 Target filters
|
|
226
|
+
|
|
227
|
+
`any_target` (creature, player or planeswalker), `creature`, `planeswalker`,
|
|
228
|
+
`creature_or_planeswalker`, `permanent`, `player`, `opponent`, `spell`, `one_of`. See
|
|
229
|
+
`docs/reference/filters.md`. A filter takes restrictions as parameters:
|
|
230
|
+
|
|
231
|
+
| Oracle words | Data |
|
|
232
|
+
| --- | --- |
|
|
233
|
+
| "you control" / "an opponent controls" | `{ "creature": { "controller": "you" } }` |
|
|
234
|
+
| "another" | `{ "creature": { "other": true } }` |
|
|
235
|
+
| "nonland", "artifact or creature" | `{ "permanent": { "not": ["land"] } }`, `{ "permanent": { "any_of": ["artifact", "creature"] } }` |
|
|
236
|
+
| "artifact creature" | `{ "creature": { "types": ["artifact"] } }` |
|
|
237
|
+
| "with power 4 or greater", "with mana value 3 or less" | `{ "creature": { "power": { "at_least": 4 } } }`, `{ "permanent": { "mana_value": { "at_most": 3 } } }` (also `toughness`) |
|
|
238
|
+
| "with mana value equal to the number of charge counters on this" | `{ "permanent": { "mana_value": { "equal": { "counters": { "of": "self", "kind": "charge" } } } } }`: a bound is any amount; a sacrificed source counts as it last existed (CR 608.2h) |
|
|
239
|
+
| "Human", "non-Doctor" | `{ "creature": { "subtypes": ["human"] } }`, `{ "creature": { "not_subtypes": ["doctor"] } }` |
|
|
240
|
+
| "legendary", "nonlegendary" | `{ "creature": { "supertypes": ["legendary"] } }`, `{ "creature": { "not_supertypes": ["legendary"] } }` |
|
|
241
|
+
| "token", "nontoken" | `{ "creature": { "token": true } }`, `{ "creature": { "token": false } }` |
|
|
242
|
+
| "with a counter on it", "with a +1/+1 counter on it" | `{ "creature": { "counters": true } }`, `{ "creature": { "counters": "+1/+1" } }` |
|
|
243
|
+
| "modified" (CR 700.9) | `{ "creature": { "modified": true } }` |
|
|
244
|
+
| "attacking", "tapped" | `{ "creature": { "attacking": true } }`, `{ "creature": { "tapped": true } }` |
|
|
245
|
+
| "land, or creature with power 6 or greater" | `{ "one_of": [{ "permanent": { "any_of": ["land"] } }, { "creature": { "power": { "at_least": 6 } } }] }` |
|
|
246
|
+
| "noncreature spell" | `{ "spell": { "not": ["creature"] } }` |
|
|
247
|
+
|
|
248
|
+
| "with flying", "with flying or reach", "without flying" | `{ "creature": { "keywords": ["flying"] } }`, `{ "creature": { "keywords": ["flying", "reach"] } }`, `{ "creature": { "not_keywords": ["flying"] } }` (current keywords: granted ones count, CR 702.1) |
|
|
249
|
+
| "of the chosen color / creature type / card type", "not of the chosen type" | `{ "creature": { "subtypes": "chosen" } }`, `{ "spell": { "colors": "chosen" } }`, `{ "permanent": { "types": "chosen" } }`, `{ "creature": { "not_subtypes": "chosen" } }` (see 4.15) |
|
|
250
|
+
|
|
251
|
+
### 4.5 Who an effect applies to: the `to` selector
|
|
252
|
+
|
|
253
|
+
| Selector | Oracle words |
|
|
254
|
+
| --- | --- |
|
|
255
|
+
| `{ "target": 0 }` | "target …" (the 1st declared target; `1` is the 2nd, …) |
|
|
256
|
+
| `"you"` | "you" ("you gain 3 life", "draw a card") |
|
|
257
|
+
| `"each_opponent"` | "each opponent" |
|
|
258
|
+
| `"self"` | "this creature" / the card's own name, on a permanent; "this card" for an ability that works from a graveyard, exile or a hand ("{4}{B}: Return this card from your graveyard to the battlefield tapped") |
|
|
259
|
+
| `"attached"` | "equipped creature" / "enchanted creature", on an Equipment or Aura |
|
|
260
|
+
| `{ "each": "creature", "controller": "you" }` | "creatures you control", "each creature you control" |
|
|
261
|
+
| `{ "each": "creature", "controller": "opponent" }` | "creatures your opponents control" |
|
|
262
|
+
| `{ "each": "creature" }` | "each creature", "all creatures" |
|
|
263
|
+
| `{ "result": 0 }` | "it" / "that card" / "the token": what effect 0 of the same list moved or created (CR 400.7j) |
|
|
264
|
+
| `"gift_recipient"` | "the chosen player" of a gift (CR 702.174a; see 4.24) |
|
|
265
|
+
|
|
266
|
+
### 4.5a Results, optional effects and delayed triggers
|
|
267
|
+
|
|
268
|
+
Every effect records the objects it moved or created (the new object ids, CR 400.7).
|
|
269
|
+
A later effect of the same effect list names them with `{ "result": n }`, where `n`
|
|
270
|
+
is the earlier effect's index (inside a mode: the index within that mode's list).
|
|
271
|
+
Lists nested in a `may` or a `delayed_trigger` see the results of their parent's
|
|
272
|
+
earlier effects.
|
|
273
|
+
|
|
274
|
+
```json
|
|
275
|
+
[{ "exile": { "to": { "target": 0 } } },
|
|
276
|
+
{ "return_to_battlefield": { "to": { "result": 0 } } }]
|
|
277
|
+
```
|
|
278
|
+
is a flicker ("exile …, then return it to the battlefield under its owner's control").
|
|
279
|
+
|
|
280
|
+
- `may` (CR 118.12): `{ "may": { "effects": [...], "then": [...], "otherwise": [...] } }`
|
|
281
|
+
is "you may [effects]. If you do, [then]. If you don't, [otherwise]." The controller
|
|
282
|
+
answers yes/no as it resolves (an `effect_choice`); yes is offered only when the
|
|
283
|
+
optional part can be done fully (CR 118.3).
|
|
284
|
+
- `delayed_trigger` (CR 603.7): `{ "delayed_trigger": { "when": { "step_begins":
|
|
285
|
+
{ "step": "end", "whose": "each" } }, "effects": [{ "sacrifice": { "to": { "result": 0 } } }] } }`
|
|
286
|
+
is "… at the beginning of the next end step". It triggers once, with the controller
|
|
287
|
+
and source of what created it; a remembered object that has changed zones is not
|
|
288
|
+
affected (CR 603.7c).
|
|
289
|
+
- `{ "was_cast": true }` as a triggered ability's `if` is "if you cast it" (CR 603.4).
|
|
290
|
+
|
|
291
|
+
### 4.5b More trigger shapes
|
|
292
|
+
|
|
293
|
+
| Shape | Data |
|
|
294
|
+
| --- | --- |
|
|
295
|
+
| "Whenever ~ becomes tapped" | `{ "becomes_tapped": {} }` |
|
|
296
|
+
| "Whenever one or more +1/+1 counters are put on ~" | `{ "counters_placed": { "counter": "+1/+1" } }` |
|
|
297
|
+
| one of your creatures is targeted by an opponent's spell or ability | `{ "becomes_target": { "subject": { "each": "creature", "controller": "you" }, "by": "opponent" } }` |
|
|
298
|
+
| "Whenever a card leaves your graveyard" / "one or more cards leave" | `{ "leaves_graveyard": {} }` / `{ "leaves_graveyard": { "batched": true } }` |
|
|
299
|
+
| "Whenever an opponent activates an ability" (not mana abilities) | `{ "activates_ability": { "controller": "opponent" } }` |
|
|
300
|
+
| "When you cast this spell" | `{ "cast": { "self": true } }` |
|
|
301
|
+
| you cast a spell that is a creature or a Spacecraft | `{ "cast": { "spell": { "one_of": [{ "spell": { "any_of": ["creature"] } }, { "spell": { "subtypes": ["spacecraft"] } }] } } }` |
|
|
302
|
+
| a number chosen as it enters (0 to 10) / spells whose mana value is that number | ability `{ "choose_number": { "min": 0, "max": 10 } }` / `"cast": { "mana_value": "chosen" }` |
|
|
303
|
+
| a nonland card name chosen as it enters / spells or sources with that name | ability `{ "choose_card_name": { "not": ["land"] } }` / `"name": "chosen"` on `cast` or `activates_ability` |
|
|
304
|
+
| triggers at most once per turn | `"limit": "once_each_turn"` on the `triggered` ability |
|
|
305
|
+
| a triggered ability that works from the graveyard ("return this card from your graveyard") | `"zone": "graveyard"` on the `triggered` ability (CR 113.6k) |
|
|
306
|
+
| "that creature", "that player", "that many" | selectors `{ "triggering": "object" }`, `{ "triggering": "player" }`, amount `{ "triggering": "amount" }` |
|
|
307
|
+
| the triggering spell is a Saga | condition `{ "triggering": { "spell": { "subtypes": ["saga"] } } }` |
|
|
308
|
+
| the targeted creature dying later this turn | `{ "delayed_trigger": { "when": { "dies": { "subject": "watched" } }, "watch": { "target": 0 }, "this_turn": true, "effects": [...] } }`; effects name the watched objects with the selector `"watched"` |
|
|
309
|
+
| each of your creatures entering for the rest of the turn | `delayed_trigger` with `"this_turn": true` (it triggers each time until the turn ends) |
|
|
310
|
+
| "at the beginning of the next combat this turn, gain control of that creature" | `{ "delayed_trigger": { "when": { "step_begins": { "step": "beginning_of_combat" } }, "this_turn": "once", "watch": { "target": 0 }, "effects": [{ "gain_control": { "to": "watched", "duration": "end_of_turn" } }] } }` (`"once"`: only the next time, and only this turn) |
|
|
311
|
+
| optional A; if it was done, a reflexive trigger B with its own target | `{ "may": { "effects": [A], "then": [{ "reflexive_trigger": { "targets": [...], "effects": [B] } }] } }` |
|
|
312
|
+
| a targeted opponent decides whether A happens; B if not | `{ "may": { "chooser": { "target": 0 }, "effects": [A], "otherwise": [B] } }` |
|
|
313
|
+
| your life gain makes your permanents' triggered abilities trigger one extra time | `{ "additional_triggers": { "cause": "you_gain_life" } }` |
|
|
314
|
+
| tapped unless its controller has two other lands / a trigger only if it came in untapped | `{ "enters_tapped_unless": { "condition": { "controls": { "each": "land", "controller": "you" }, "at_least": 2 } } }` / `{ "enters": { "untapped": true } }` |
|
|
315
|
+
| returned to the battlefield carrying a +1/+1 counter | `"counters": { "+1/+1": 1 }` on `return_to_battlefield` |
|
|
316
|
+
|
|
317
|
+
Zone-change and keyword-action triggers. Leaving the battlefield and sacrificing look back in time
|
|
318
|
+
(CR 603.10a): the permanent is matched as it last existed, its own ability triggers although it has
|
|
319
|
+
left, its effects read its last known counters and power (`{ "counters": { "of": "self" } }`), and
|
|
320
|
+
permanents that leave at the same time (one effect, or state-based actions) see each other leave.
|
|
321
|
+
`"batched": true` is "one or more": one trigger per event batch, and `{ "triggering": "amount" }` is
|
|
322
|
+
how many occurrences the batch had.
|
|
323
|
+
|
|
324
|
+
| Shape | Data |
|
|
325
|
+
| --- | --- |
|
|
326
|
+
| "When this leaves the battlefield" / "is put into exile from the battlefield" | `{ "leaves_battlefield": {} }` / `{ "leaves_battlefield": { "to": "exile" } }` |
|
|
327
|
+
| "Whenever another creature you control leaves the battlefield" | `{ "leaves_battlefield": { "subject": { "each": "creature", "controller": "you", "other": true } } }` |
|
|
328
|
+
| "When this enters or leaves the battlefield" | `{ "any_of": [{ "enters": {} }, { "leaves_battlefield": {} }] }` |
|
|
329
|
+
| "Whenever you sacrifice another permanent" / "a Food" | `{ "sacrifices": { "subject": { "each": "permanent", "controller": "you", "other": true } } }` / `"each": { "artifact": { "subtypes": ["food"] } }` |
|
|
330
|
+
| "Whenever you discard one or more cards, create that many …" | `{ "discards": { "batched": true } }`, amount `{ "triggering": "amount" }` |
|
|
331
|
+
| "Whenever you cycle a card" | `{ "cycles": {} }` (a cycled card is also discarded: `discards` triggers too) |
|
|
332
|
+
| "Whenever you scry or surveil" | `{ "any_of": [{ "scries": {} }, { "surveils": {} }] }` (scry 0 isn't a scry) |
|
|
333
|
+
| "Whenever equipped creature becomes untapped" | `{ "becomes_untapped": { "subject": "attached" } }` |
|
|
334
|
+
| "Whenever one or more creature cards are put into your graveyard from anywhere" | `{ "put_into": { "zone": "graveyard", "card": "creature", "batched": true } }` |
|
|
335
|
+
| "Whenever one or more cards are put into exile from graveyards and/or the battlefield" | `{ "put_into": { "zone": "exile", "from": ["graveyard", "battlefield"], "controller": "any", "batched": true } }` |
|
|
336
|
+
| "during your turn" | `"if": { "your_turn": true }` on the `triggered` ability |
|
|
337
|
+
|
|
338
|
+
```json
|
|
339
|
+
{ "triggered": { "when": { "leaves_battlefield": {} }, "targets": [{ "creature": { "controller": "you" } }],
|
|
340
|
+
"effects": [{ "add_counters": { "counter": "+1/+1", "amount": { "counters": { "of": "self", "kind": "+1/+1" } },
|
|
341
|
+
"to": { "target": 0 } } }] } }
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
### 4.6 Effects
|
|
345
|
+
|
|
346
|
+
See `docs/reference/effects.md` for the generated list. Currently:
|
|
347
|
+
|
|
348
|
+
| Effect | Oracle text | Data |
|
|
349
|
+
| --- | --- | --- |
|
|
350
|
+
| `deal_damage` | "deals N damage to …" | `{ "deal_damage": { "amount": N, "to": … } }` |
|
|
351
|
+
| `pump` | "gets +X/+Y until end of turn" | `{ "pump": { "power": X, "toughness": Y, "to": … } }` |
|
|
352
|
+
| `pump` | "get -1/-0 until your next turn" | `{ "pump": { "power": -1, "toughness": 0, "duration": "until_your_next_turn", "to": … } }` (ends as your next turn begins, CR 611.2a) |
|
|
353
|
+
| `add_counters` | "put N +1/+1 counters on …" | `{ "add_counters": { "counter": "+1/+1", "amount": N, "to": … } }` (any counter name: `+1/+1`, `stun`, `shield`, `oil`, a keyword; see `docs/reference/counters.md`) |
|
|
354
|
+
| `gain_life` | "you gain N life" | `{ "gain_life": { "amount": N } }` (`to` defaults to `"you"`) |
|
|
355
|
+
| `lose_life` | "each opponent loses N life" | `{ "lose_life": { "amount": N, "to": "each_opponent" } }` |
|
|
356
|
+
| `draw` | "draw N cards" | `{ "draw": { "amount": N } }` (`to` defaults to `"you"`) |
|
|
357
|
+
| `destroy` | "destroy target …" | `{ "destroy": { "to": … } }` |
|
|
358
|
+
| `counter` | "counter target spell" | `{ "counter": { "to": { "target": 0 } } }` |
|
|
359
|
+
|
|
360
|
+
### 4.7 Mana effects, X and cost changes
|
|
361
|
+
|
|
362
|
+
| Oracle text | Data |
|
|
363
|
+
| --- | --- |
|
|
364
|
+
| "Add {R}{R}{R}." (a spell or trigger, not a mana ability) | `{ "add_mana": { "produce": "{R}{R}{R}" } }` |
|
|
365
|
+
| "Add two mana of any one color." | `{ "add_mana": { "amount": 2, "color": "any_one" } }` |
|
|
366
|
+
| "Add two mana in any combination of colors." | `{ "add_mana": { "amount": 2, "color": "any" } }` |
|
|
367
|
+
| "Add {C}{C}. Spend this mana only to cast spells." | `{ "add_mana": { "produce": "{C}{C}", "spend_only": { "spells": "card" } } }` |
|
|
368
|
+
| `{X}` in the cost, "deals X damage" | `"mana_cost": "{X}{R}"`, `{ "deal_damage": { "amount": "x", ... } }` |
|
|
369
|
+
| "... costs {2} less to cast if it targets a tapped creature." | `{ "cost_reduction": { "amount": 2, "if": { "targets": { "creature": { "tapped": true } } } } }` |
|
|
370
|
+
| "... costs {1} less to cast if a creature is attacking you." | `{ "cost_reduction": { "amount": 1, "if": { "attacked": true } } }` |
|
|
371
|
+
| "Affinity for artifacts" | `{ "cost_reduction": { "amount": { "count": { "each": "artifact", "controller": "you" } } } }` |
|
|
372
|
+
| "Noncreature spells you cast cost {1} less to cast." | `{ "cost_modifier": { "spells": { "not": ["creature"] }, "reduce": 1 } }` |
|
|
373
|
+
| "Spells your opponents cast cost {1} more to cast." | `{ "cost_modifier": { "caster": "opponent", "increase": 1 } }` |
|
|
374
|
+
| "This ability costs {1} less to activate for each land you control." | `"cost_reduction": { "amount": {...} }` inside the `activated` ability |
|
|
375
|
+
|
|
376
|
+
An activated ability without targets that adds mana is a *mana ability* (CR 605.1a):
|
|
377
|
+
use `mana`, not `activated` with `add_mana` (validation rejects it). In an
|
|
378
|
+
enters-the-battlefield trigger, `"x"` is the X the permanent was cast with (CR 107.3m).
|
|
379
|
+
|
|
380
|
+
### 4.8 Auras and Equipment
|
|
381
|
+
|
|
382
|
+
Give the card the subtype `aura` (enchantments only) or `equipment` (artifacts only).
|
|
383
|
+
|
|
384
|
+
| Oracle text | Data |
|
|
385
|
+
| --- | --- |
|
|
386
|
+
| "Enchant creature" | `{ "enchant": "creature" }`. Any permanent filter works, e.g. `{ "enchant": { "permanent": { "any_of": ["artifact", "creature"] } } }`. Every Aura needs exactly one. The Aura spell targets with it (CR 303.4a), so don't add a `spell` ability. |
|
|
387
|
+
| "Equip {2}" | `{ "equip": { "cost": [{ "mana": "{2}" }] } }`. Only Equipment have it. |
|
|
388
|
+
| "Equipped creature gets +2/+0 and has trample." | `{ "static": { "affects": "attached", "pump": { "power": 2, "toughness": 0 }, "keywords": ["trample"] } }` |
|
|
389
|
+
| "Whenever equipped creature attacks, …" | `{ "triggered": { "when": { "attacks": { "subject": "attached" } }, … } }` |
|
|
390
|
+
| "When this Aura enters, enchanted creature gains … until end of turn." | an `enters` trigger with `{ "grant": { …, "to": "attached" } }` |
|
|
391
|
+
| "attach it to target creature you control" | `{ "attach": { "to": { "target": 0 } } }` |
|
|
392
|
+
| "create an Equipment token …, then attach it to this creature" | `create_token`, then `{ "attach": { "to": "self", "attachment": { "result": 0 } } }` |
|
|
393
|
+
| "… with a Communicator attached to it" | the filter restriction `{ "creature": { "attached_subtypes": ["communicator"] } }` |
|
|
394
|
+
|
|
395
|
+
Not supported yet: Auras that enchant players, Equipment that enters attached or
|
|
396
|
+
equips for a non-mana cost, and granted activated abilities ("equipped creature has
|
|
397
|
+
'{T}: …'"). Granted triggered abilities are the static `abilities` list (4.11).
|
|
398
|
+
|
|
399
|
+
### 4.9 Station cards, Sagas and other set mechanics
|
|
400
|
+
|
|
401
|
+
- **Station cards** (CR 702.184, 722). Put `"station"` in `keywords`: that is the
|
|
402
|
+
station activated ability. Each `N+ | …` striation is a `station_symbol` ability.
|
|
403
|
+
The card's power/toughness box goes on the symbol it's printed with (the one the
|
|
404
|
+
reminder text says makes it an artifact creature, else the last one), **not** on the card,
|
|
405
|
+
because a station card has no P/T until it's a creature (CR 722.2c):
|
|
406
|
+
|
|
407
|
+
```json
|
|
408
|
+
{ "name": "Example Skiff", "mana_cost": "{2}", "types": ["artifact"], "subtypes": ["spacecraft"],
|
|
409
|
+
"keywords": ["station"],
|
|
410
|
+
"abilities": [{ "station_symbol": { "at": 4, "power": 3, "toughness": 3, "keywords": ["flying"],
|
|
411
|
+
"abilities": [] } }] }
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
Abilities printed after the first station symbol belong to its striation: list
|
|
415
|
+
them in that symbol's `abilities`.
|
|
416
|
+
- **Sagas** (CR 715). `"subtypes": ["saga"]` on an enchantment, and one `chapter`
|
|
417
|
+
ability per chapter number. "I, II — effect" is two chapter abilities (CR 715.2c):
|
|
418
|
+
|
|
419
|
+
```json
|
|
420
|
+
{ "chapter": { "chapter": 1, "targets": ["creature"],
|
|
421
|
+
"effects": [{ "deal_damage": { "amount": 2, "to": { "target": 0 } } }] } }
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
The lore counters, the precombat-main counter and the sacrifice are automatic.
|
|
425
|
+
- **Face a dilemma** (CR 700.17): the trigger `{ "faces_dilemma": {} }` ("whenever
|
|
426
|
+
you face a dilemma"; `"controller": "opponent"` or `"any"` for others).
|
|
427
|
+
- **Assimilate** (CR 701.72): the effect `{ "assimilate": { "to": … } }`. "Target
|
|
428
|
+
creature card from a graveyard" is the filter `{ "graveyard_card": { "any_of": ["creature"] } }`.
|
|
429
|
+
"Assimilate a creature card exiled with this. If you can't, exile up to one target creature an
|
|
430
|
+
opponent controls" chooses among the linked cards as it resolves (`choose: true`, `for` a card spec),
|
|
431
|
+
and the exile only happens when the assimilate put nothing onto the battlefield (CR 701.72c, 607.2a;
|
|
432
|
+
the target is still chosen when the trigger is put on the stack, CR 603.3d):
|
|
433
|
+
|
|
434
|
+
```json
|
|
435
|
+
{ "triggered": { "when": { "any_of": [{ "enters": {} }, { "attacks": {} }] },
|
|
436
|
+
"targets": [{ "filter": { "creature": { "controller": "opponent" } }, "optional": true }],
|
|
437
|
+
"effects": [
|
|
438
|
+
{ "assimilate": { "to": "exiled_with", "for": { "types": ["creature"] }, "choose": true } },
|
|
439
|
+
{ "exile": { "to": { "target": 0 }, "link": true,
|
|
440
|
+
"if": { "amount": { "count": { "result": 0 } }, "at_most": 0 } } }] } }
|
|
441
|
+
```
|
|
442
|
+
- **"As this enters" choices on permanents put onto the battlefield by effects** (CR 614.12a).
|
|
443
|
+
Nothing to write: when `return_to_battlefield`, `search_library`, `dig`, `reveal_until`,
|
|
444
|
+
`choose_cards` (`put: "battlefield"`), `assimilate` or `create_token` puts a permanent with
|
|
445
|
+
`choose_number`, `choose_card_name` or `enters_tapped_unless_pay_life` onto the battlefield,
|
|
446
|
+
its controller-to-be answers an `enter_choice` (with `effect: true`) for each one in turn, then
|
|
447
|
+
they all enter at once. "Exile the top four cards of your library; put all permanent cards
|
|
448
|
+
exiled this way onto the battlefield under your control":
|
|
449
|
+
|
|
450
|
+
```json
|
|
451
|
+
[{ "exile_top": { "amount": 4 } },
|
|
452
|
+
{ "return_to_battlefield": { "to": { "result": 0 }, "under": "you" } }]
|
|
453
|
+
```
|
|
454
|
+
- **Strange new worlds** is an ability word: drop it. "The number of differently
|
|
455
|
+
named lands you control" is the amount
|
|
456
|
+
`{ "distinct_names": { "each": "land", "controller": "you" } }`.
|
|
457
|
+
|
|
458
|
+
### 4.10 Casting and playing from other zones
|
|
459
|
+
|
|
460
|
+
- **Beam me up** (CR 702.196): `{ "beam_me_up": { "cost": "{3}{W}" } }` on an instant
|
|
461
|
+
or sorcery. A permanent that gives the cards in its controller's graveyard beam me up
|
|
462
|
+
during their main phase, for each card's own mana cost, has the ability
|
|
463
|
+
`"grants_beam_me_up"`; that permanent can't be the creature returned for spells cast
|
|
464
|
+
its way. A creature that can never be returned for a beam me up cost has the keyword
|
|
465
|
+
`"cant_be_beamed_up"`.
|
|
466
|
+
- **Keyword alternative costs** (CR 118.9): each is an ability with a `cost`, either a
|
|
467
|
+
mana string or a list of costs like kicker's (mana plus chosen costs such as
|
|
468
|
+
`tap_permanents`). A card with one gets an extra cast option
|
|
469
|
+
`{type: "cast", object: id, via: "alt:<ability ref>"}` while the keyword works:
|
|
470
|
+
- `{ "flashback": { "cost": "{2}{R}" } }` (CR 702.34): instant or sorcery, from your
|
|
471
|
+
graveyard; it's exiled whenever it would leave the stack. "Flashback—{1}{U}, tap two
|
|
472
|
+
untapped creatures you control" is
|
|
473
|
+
`{ "flashback": { "cost": [{ "mana": "{1}{U}" }, { "tap_permanents": { "count": 2 } }] } }`.
|
|
474
|
+
- `{ "harmonize": { "cost": "{4}{G}" } }` (CR 702.180): from your graveyard; while
|
|
475
|
+
casting you may tap one untapped creature you control (a `choose_cost` decision,
|
|
476
|
+
`{type: "cost"}` taps none) to reduce the total cost by its power. Exiled like flashback.
|
|
477
|
+
- `{ "mayhem": { "cost": "{1}{R}" } }` (CR 702.187): from your graveyard if you
|
|
478
|
+
discarded the card this turn. `"mayhem"` without a cost lets you play it (a land too)
|
|
479
|
+
from your graveyard for its normal costs.
|
|
480
|
+
- `{ "warp": { "cost": "{1}{R}" } }` (CR 702.185): from your hand; the permanent is
|
|
481
|
+
exiled at the beginning of the next end step, and its owner may cast it from exile
|
|
482
|
+
after that turn (`via: "warped"`) for as long as it stays there.
|
|
483
|
+
- `{ "evoke": { "cost": "{R/W}{R/W}" } }` (CR 702.74): on a permanent card; it comes
|
|
484
|
+
with "When this enters, if its evoke cost was paid, sacrifice it".
|
|
485
|
+
- `{ "web_slinging": { "cost": "{2}{G}" } }` (CR 702.188): also return a tapped
|
|
486
|
+
creature you control to its owner's hand (a `choose_cost` decision).
|
|
487
|
+
- `{ "sneak": { "cost": "{1}{B}" } }` (CR 702.190): during your declare blockers step,
|
|
488
|
+
at instant speed, also return an unblocked attacking creature you control; a
|
|
489
|
+
permanent spell enters tapped and attacking what that creature attacked.
|
|
490
|
+
|
|
491
|
+
**Convoke** (CR 702.51) is the keyword `"convoke"`: while paying, the `pay_cost`
|
|
492
|
+
option `{type: "convoke", object: id}` taps an untapped creature you control to pay
|
|
493
|
+
{1} or one mana of its color. **Improvise** (CR 702.126) is the keyword `"improvise"`:
|
|
494
|
+
`{type: "improvise", object: id}` taps an untapped artifact you control to pay {1} of
|
|
495
|
+
the generic part.
|
|
496
|
+
|
|
497
|
+
How a spell was cast is a condition (CR 400.7d): `{ "cast_with": "evoke" }` (its
|
|
498
|
+
keyword cost was paid), `{ "cast_from": "graveyard" }` (also `hand`, `exile`,
|
|
499
|
+
`library`, `outside`; "from anywhere other than your hand" is
|
|
500
|
+
`{ "not": { "cast_from": "hand" } }`), and `{ "cast_with_this_turn": "warp" }` ("if a
|
|
501
|
+
spell was warped this turn"). The permanent a spell becomes, and its triggered
|
|
502
|
+
abilities, remember it. "Draw a card. If this spell was cast from a graveyard, draw
|
|
503
|
+
two cards instead. Flashback {3}{U}":
|
|
504
|
+
```json
|
|
505
|
+
{ "name": "Second Glance", "mana_cost": "{1}{U}", "types": ["sorcery"],
|
|
506
|
+
"abilities": [
|
|
507
|
+
{ "spell": { "effects": [
|
|
508
|
+
{ "draw": { "amount": 1, "if": { "not": { "cast_from": "graveyard" } } } },
|
|
509
|
+
{ "draw": { "amount": 2, "if": { "cast_from": "graveyard" } } } ] } },
|
|
510
|
+
{ "flashback": { "cost": "{3}{U}" } } ] }
|
|
511
|
+
```
|
|
512
|
+
- **A card castable from its owner's graveyard or exile**:
|
|
513
|
+
`{ "play_from": { "zone": "graveyard" } }`; add `"enters_with": { "finality": 1 }`
|
|
514
|
+
when a permanent spell cast that way gets a finality counter.
|
|
515
|
+
- **A permanent's permission** for its controller's own cards: give `cards` a card
|
|
516
|
+
spec. Casting an artifact spell from the top of the library once per turn (on your
|
|
517
|
+
own turns) is `{ "play_from": { "zone": "library_top", "cards": { "types":
|
|
518
|
+
["artifact"] }, "once_each_turn": true } }`; playing lands from the top uses
|
|
519
|
+
`"cards": { "types": ["land"] }, "play": "play"`. Seeing the top card of your library
|
|
520
|
+
at all times (CR 401.5) is the ability `"look_at_top"`.
|
|
521
|
+
- **Several zones, and outside the game** (CR 400.11a): `zones` lists the zones
|
|
522
|
+
instead of `zone`, and `"outside"` means the cards the player owns outside the game
|
|
523
|
+
(the deck's `outside` list, see [Decks](#6-decks)). With `once_each_turn`, all the
|
|
524
|
+
zones share one use per turn. "Once during each of your turns, you may play a
|
|
525
|
+
permanent card you own from outside the game or from your graveyard":
|
|
526
|
+
```json
|
|
527
|
+
{ "play_from": { "zones": ["outside", "graveyard"], "cards": { "permanent": true },
|
|
528
|
+
"play": "play", "once_each_turn": true } }
|
|
529
|
+
```
|
|
530
|
+
A land played this way uses the turn's land play (CR 305.2).
|
|
531
|
+
- **Impulse draw**: `exile_top` (or `exile_until` with `"until": "nonland"`) followed by
|
|
532
|
+
`{ "play_permission": { "to": { "result": 0 }, "duration": "end_of_your_next_turn" } }`
|
|
533
|
+
(`end_of_turn` for the current turn only, `while_exiled` for as long as the cards stay
|
|
534
|
+
in exile). `not_types: ["land"]` keeps only the nonland card an exile-until found;
|
|
535
|
+
`"choose": "one_per_owner"` with `"for": { "types": ["land"] }` makes the controller
|
|
536
|
+
pick one matching card per owner among the exiled ones.
|
|
537
|
+
- **Exiled face down** (CR 406.3): `"face_down": true` on `exile_top`; `looker` names the
|
|
538
|
+
players who may look at the cards (default: you). A face-down card has no
|
|
539
|
+
characteristics, its id doesn't name it (`face_down#12`), and views and events show
|
|
540
|
+
it only to the lookers. `play_permission` takes `to_player` (see below; a face-down
|
|
541
|
+
card needs a player who may look at it, CR 406.3b) and `any_mana_type: true` ("mana of any type can be spent to cast those spells", CR 609.4b).
|
|
542
|
+
The card is turned face up as it's played. "Target opponent exiles the top card of your
|
|
543
|
+
library face down; you exile the top two cards of theirs face down; each card's exiler
|
|
544
|
+
may play it while it stays exiled, spending mana as though it were mana of any type":
|
|
545
|
+
|
|
546
|
+
```json
|
|
547
|
+
{ "spell": { "targets": ["opponent"], "effects": [
|
|
548
|
+
{ "exile_top": { "amount": 1, "face_down": true, "looker": { "target": 0 } } },
|
|
549
|
+
{ "exile_top": { "amount": 2, "to": { "target": 0 }, "face_down": true } },
|
|
550
|
+
{ "play_permission": { "to": { "result": 0 }, "to_player": { "target": 0 }, "duration": "while_exiled", "any_mana_type": true } },
|
|
551
|
+
{ "play_permission": { "to": { "result": 1 }, "duration": "while_exiled", "any_mana_type": true } } ] } }
|
|
552
|
+
```
|
|
553
|
+
- **Who may play it, and for how much** (CR 108.3, 601.2f): `play_permission` takes
|
|
554
|
+
`to_player`, which is `"controller"` (the default), `"owner"` (each card's owner) or a
|
|
555
|
+
player selector such as `{ "target": 0 }`. It also takes `cost_increase`: a spell
|
|
556
|
+
cast with this permission costs that much more generic mana. This applies only when
|
|
557
|
+
the permission is the one used to cast it. "Look at target opponent's hand and exile
|
|
558
|
+
a nonland card from it. For as long as that card remains exiled, its owner may play
|
|
559
|
+
it. A spell cast this way costs {2} more":
|
|
560
|
+
```json
|
|
561
|
+
[{ "look_at_hand": { "to": { "target": 0 } } },
|
|
562
|
+
{ "choose_cards": { "from": { "hand": { "target": 0 } }, "for": { "not_types": ["land"] }, "put": "exile" } },
|
|
563
|
+
{ "play_permission": { "to": { "result": 1 }, "to_player": "owner", "duration": "while_exiled",
|
|
564
|
+
"cost_increase": 2 } }]
|
|
565
|
+
```
|
|
566
|
+
- **Casting while resolving without paying mana costs** (CR 608.2g):
|
|
567
|
+
`{ "cast_free": { "to": { "result": 0 }, "max_mana_value": 3, "count": "any" } }`;
|
|
568
|
+
with no `to` it offers matching cards from your hand. **Discover N** (CR 701.57) is
|
|
569
|
+
`{ "discover": { "amount": 5 } }`.
|
|
570
|
+
|
|
571
|
+
### 4.11 Amounts, conditions, "this turn" and static abilities
|
|
572
|
+
|
|
573
|
+
The full lists are in `lib/enjine/mechanics/values.rb` (`Amount`, `Condition`).
|
|
574
|
+
|
|
575
|
+
| Oracle words | Data |
|
|
576
|
+
| --- | --- |
|
|
577
|
+
| "+X/+X, where X is its power" | `{ "pump": { "power": { "power": "self" }, "toughness": { "power": "self" }, "to": … } }` |
|
|
578
|
+
| "-X/-X" | `{ "times": [<amount>, -1] }`; also `plus`, `divide` (rounds down, CR 107.1a) |
|
|
579
|
+
| "the number of creature types among …" | `{ "distinct_types": <selector> }` |
|
|
580
|
+
| "the number of counters on it" | `{ "counters": { "of": "self", "kind": "quest" } }` (no `kind`: every counter) |
|
|
581
|
+
| "cards in your graveyard", "greatest power among …" | `{ "graveyard_size": "you" }`, `{ "greatest_power": <selector> }` |
|
|
582
|
+
| "life you gained / that player lost this turn" | `{ "life_gained_this_turn": "you" }`, `{ "life_lost_this_turn": "each_opponent" }` |
|
|
583
|
+
| an amount an earlier effect changed (life it made a player lose) | `{ "this_way": n }` (effect n of the same list) |
|
|
584
|
+
| N or more cards drawn by an earlier effect; "if you do" after a mandatory effect | `{ "amount": { "count": { "result": n } }, "at_least": 3 }` |
|
|
585
|
+
| a trigger that also needs total power N or more of your creatures | `"while": { "amount": { "power": { "each": "creature", "controller": "you" } }, "at_least": 8 }` on a trigger (checked only as it triggers) |
|
|
586
|
+
| "if a creature died this turn", "if you've cast a noncreature spell this turn" | `{ "amount": { "died_this_turn": { "each": "creature" } }, "at_least": 1 }`, `{ "spells_cast_this_turn": { "by": "you", "matching": { "spell": { "not": ["creature"] } } } }` |
|
|
587
|
+
| raid: "if you attacked this turn" | `{ "you_attacked_this_turn": true }` |
|
|
588
|
+
| "if you attacked with three or more Merfolk this turn" | `{ "amount": { "attackers_this_turn": { "each": { "creature": { "subtypes": ["merfolk"] } }, "controller": "you" } }, "at_least": 3 }` |
|
|
589
|
+
| "if a permanent left the battlefield under your control this turn" / "a nonland permanent left the battlefield this turn" | `{ "amount": { "left_this_turn": { "controller": "you" } }, "at_least": 1 }` / `{ "left_this_turn": { "each": { "permanent": { "not": ["land"] } } } }` |
|
|
590
|
+
| "the number of permanents you've sacrificed this turn" | `{ "sacrificed_this_turn": { "controller": "you" } }` |
|
|
591
|
+
| "if one or more cards were put into exile this turn" | `{ "amount": { "exiled_this_turn": {} }, "at_least": 1 }` |
|
|
592
|
+
| "if you've drawn two or more cards this turn", "each card you've discarded this turn" | `{ "amount": { "cards_drawn_this_turn": "you" }, "at_least": 2 }`, `{ "cards_discarded_this_turn": "you" }` |
|
|
593
|
+
| "if you gained life this turn" | `{ "amount": { "life_gained_this_turn": "you" }, "at_least": 1 }` |
|
|
594
|
+
| "if a creature died under your control this turn" | `{ "amount": { "died_this_turn": { "each": "creature", "controller": "you" } }, "at_least": 1 }` |
|
|
595
|
+
| the Nth resolution of this ability this turn | `{ "nth_resolution": 2 }` |
|
|
596
|
+
| "if it isn't a creature", "if it attacked this turn" | `{ "not": { "source": "creature" } }`, `{ "attacked_this_turn": true }` |
|
|
597
|
+
| "its controller creates …" | `"to": { "controller_of": { "target": 0 } }` |
|
|
598
|
+
| a fixed mana symbol added a computed number of times | `{ "add_mana": { "produce": "{G}", "amount": <amount> } }` |
|
|
599
|
+
|
|
600
|
+
Static abilities (`static`) take `"affects": "self"` and an `if` ("as long as …",
|
|
601
|
+
"during your turn": `{ "your_turn": true }`); `pump` amounts may be computed ("gets
|
|
602
|
+
+1/+0 for each …"); `creature_types` sets the creature types;
|
|
603
|
+
`abilities` grants triggered or mana abilities (each matching permanent has them). A `*` power or toughness is an amount in the card data (`"power": { "count":
|
|
604
|
+
… }`, CR 604.3). Setting creature types until end of turn is `set_creature_types`; setting
|
|
605
|
+
base power and toughness is `set_base_power_toughness` (`duration: "indefinite"` lasts while it stays).
|
|
606
|
+
A token's or color-indicator card's colors are `"colors": ["R"]`; filters take `"colors"`.
|
|
607
|
+
|
|
608
|
+
### 4.12 Libraries, hands, graveyards and edicts
|
|
609
|
+
|
|
610
|
+
Generic shapes (N, [quality] and [zone] stand for whatever the card says):
|
|
611
|
+
|
|
612
|
+
| Shape | Data |
|
|
613
|
+
| --- | --- |
|
|
614
|
+
| look at the top N; you may reveal a [quality] card among them and put it into your hand; the rest go to the bottom in a random order | `{ "dig": { "amount": N, "for": spec, "optional": true, "reveal": "chosen", "put": "hand" } }` |
|
|
615
|
+
| look at the top N; one goes to your hand, the rest to your graveyard | `{ "dig": { "amount": N, "rest": "graveyard" } }` |
|
|
616
|
+
| reveal the top N; every [quality] card goes onto the battlefield tapped | `{ "dig": { "amount": N, "reveal": "all", "for": spec, "count": "all", "put": "battlefield", "tapped": true } }` |
|
|
617
|
+
| the rest go to the bottom in any order | `"rest": "bottom_any"` |
|
|
618
|
+
| reveal from the top until N [quality] cards are revealed; put them into [zone] | `{ "reveal_until": { "for": spec, "count": N, "put": "battlefield" } }` |
|
|
619
|
+
| exile the top N; the permanent cards exiled this way enter the battlefield | `exile_top`, then `{ "return_to_battlefield": { "to": { "result": 0 }, "under": "you" } }` |
|
|
620
|
+
| mill N; you may put a [quality] card from among them into your hand | `mill`, then `{ "choose_cards": { "from": { "result": 0 }, "for": spec, "optional": true, "put": "hand" } }` |
|
|
621
|
+
| return a [quality] card from your graveyard to your hand; if you can't, [effect] | `{ "choose_cards": { "from": { "graveyard": "you" }, "for": spec, "put": "hand", "otherwise": [ … ] } }` |
|
|
622
|
+
| look at a player's hand; you may choose a [quality] card; if you do, [effect] | `{ "choose_cards": { "from": { "hand": { "target": 0 } }, "look": true, "for": spec, "optional": true, "put": "exile", "then": [ … ] } }` |
|
|
623
|
+
| each player sacrifices a [quality] permanent of their choice | `{ "sacrifice_choice": { "filter": filter, "to": { "each": "player" } } }` (`"count": "any"`: any number) |
|
|
624
|
+
| exile a target card in a graveyard; return a target card from your graveyard to your hand | `exile` / `return_to_hand` with a `graveyard_card` target |
|
|
625
|
+
| its owner puts it on the top or bottom of their library, as they choose | `{ "put_in_library": { "to": { "target": 0 }, "position": "owner_choice" } }` |
|
|
626
|
+
| cards exiled with this; exile until this leaves the battlefield | `exile` with `"link": true` / `"until_source_leaves": true`; the selector `"exiled_with"` |
|
|
627
|
+
| that permanent's controller may search their library | `search_library` with `"searcher": { "controller_of": { "target": 0 } }, "may": true` |
|
|
628
|
+
| a permanent explores | `{ "explore": { "to": … } }` |
|
|
629
|
+
| you return a [quality] permanent you control to its owner's hand (chosen, not targeted) | `{ "return_choice": { "filter": { "permanent": { "any_of": ["land"], "controller": "you" } } } }` (same `count` and `to` as `sacrifice_choice`) |
|
|
630
|
+
|
|
631
|
+
### 4.13 Turn structure: untap step, land plays and phasing
|
|
632
|
+
|
|
633
|
+
| Shape | Data |
|
|
634
|
+
| --- | --- |
|
|
635
|
+
| "You may play an additional land on each of your turns" (CR 305.2) | ability `"additional_land_plays"` (each instance is one more land play; lands put onto the battlefield by effects don't count) |
|
|
636
|
+
| "You may choose not to untap this during your untap step" (CR 502.3) | ability `"may_not_untap"` (its controller answers an `untap_choice` decision in their untap step while it's tapped) |
|
|
637
|
+
| phasing (CR 702.26a) | keyword `"phasing"` |
|
|
638
|
+
| "[permanents] phase out" (CR 702.26) | `{ "phase_out": { "to": { "each": { "permanent": { "any_of": ["artifact", "creature"] } } } } }` |
|
|
639
|
+
| "Permanents can't phase in" | ability `"cant_phase_in"` |
|
|
640
|
+
| "for as long as this remains tapped, … triggered abilities trigger an additional time" (CR 603.2d, 611.2b) | `{ "additional_triggers": { "to": { "target": 0 }, "duration": "while_source_tapped" } }` |
|
|
641
|
+
| "when one or more [kind] counters are put on this for the first time each turn" | `{ "counters_placed": { "counter": "charge", "first_time_this_turn": true } }` |
|
|
642
|
+
| "it becomes a [type] with base power and toughness N/M and gains '…'" (layers 4, 6, 7b) | `become`, below |
|
|
643
|
+
|
|
644
|
+
A phased-out permanent keeps its id, counters and attachments but is treated as though it
|
|
645
|
+
doesn't exist (the view lists it under `phased_out`). It phases in during the untap step of
|
|
646
|
+
the player who controlled it as it phased out; Auras and Equipment attached to it phase out and
|
|
647
|
+
back in with it.
|
|
648
|
+
|
|
649
|
+
```json
|
|
650
|
+
{ "become": { "creature_types": ["goblin", "scout"],
|
|
651
|
+
"base_power_toughness": { "power": 3, "toughness": 4 },
|
|
652
|
+
"abilities": ["additional_land_plays",
|
|
653
|
+
{ "triggered": { "when": { "becomes_tapped": {} },
|
|
654
|
+
"effects": [{ "surveil": { "amount": 1 } }] } }],
|
|
655
|
+
"duration": "indefinite", "to": "self" } }
|
|
656
|
+
```
|
|
657
|
+
|
|
658
|
+
`creature_types` replaces the creature types, `abilities` may hold triggered, mana,
|
|
659
|
+
`play_from` (for other cards) and `additional_land_plays` abilities, and `duration` is
|
|
660
|
+
`end_of_turn` (default) or `indefinite` (for as long as it stays on the battlefield). An
|
|
661
|
+
activated ability that only works while it has a subtype uses
|
|
662
|
+
`"only_if": { "source": { "creature": { "subtypes": ["rogue"] } } }`.
|
|
663
|
+
|
|
664
|
+
### 4.14 Copies (CR 707)
|
|
665
|
+
|
|
666
|
+
A copy has the **copiable values** of what it copies: the values printed on its card,
|
|
667
|
+
as changed by other copy effects and their exceptions, and nothing else: no counters,
|
|
668
|
+
tapped status, damage or other effects (CR 707.2). Every copy primitive names what it
|
|
669
|
+
copies with `of` (a selector; `"self"` is the source in any zone, even a card that
|
|
670
|
+
exiled itself from a graveyard to pay the cost) and takes the same `except` object for
|
|
671
|
+
"except …" (CR 707.9a-b), which a copy of the copy keeps:
|
|
672
|
+
`{ "legendary": false, "keywords": ["menace"], "types": [...], "subtypes": [...],
|
|
673
|
+
"supertypes": [...], "name": "…", "power": 1, "toughness": 1, "colors": [] }`.
|
|
674
|
+
|
|
675
|
+
| Oracle words | Data |
|
|
676
|
+
| --- | --- |
|
|
677
|
+
| "create a token that's a copy of target creature, except it isn't legendary" | `{ "create_token_copy": { "of": { "target": 0 }, "except": { "legendary": false } } }` (`amount`, `tapped`, `to`: the players who create them) |
|
|
678
|
+
| "That token gains haste. Sacrifice it at the beginning of the next end step." | `grant` and `delayed_trigger` with `{ "result": n }` (the tokens are the effect's result) |
|
|
679
|
+
| "exile target creature card from your graveyard, create a token that's a copy of it" | `exile`, then `{ "create_token_copy": { "of": { "result": 0 } } }` |
|
|
680
|
+
| "you may have this creature enter as a copy of any artifact or creature on the battlefield …, with the same counters" | `{ "enter_as_copy": { "of": { "permanent": { "any_of": ["artifact", "creature"] } }, "except": { "legendary": false }, "with_counters": true } }` |
|
|
681
|
+
| "… becomes a copy of target Equipment until end of turn" | `{ "become_copy": { "to": …, "of": { "target": 0 } } }` (`"duration": "permanent"` while it stays) |
|
|
682
|
+
| "When you cast this spell, copy it." | a `{ "cast": { "self": true } }` trigger with `{ "copy_spell": { "of": "self" } }` |
|
|
683
|
+
| "copy target instant or sorcery spell; you may choose new targets for the copy" | `{ "copy_spell": { "of": { "target": 0 }, "new_targets": true } }` |
|
|
684
|
+
| "copy that card X times; you may cast the copies without paying their mana costs" | `{ "copy_card": { "of": { "result": 0 }, "amount": "x" } }`, then `cast_free` with `"to": { "result": 1 }, "count": "any"` |
|
|
685
|
+
| "create a copy of this card; you may cast the copy" (paying its costs) | `{ "copy_card": { "of": "self" } }`, then `{ "cast": { "to": { "result": 0 } } }` |
|
|
686
|
+
| "that card gains '…'" (also an exiled card) | `{ "grant_ability": { "to": { "result": 0 }, "ability": { … } } }` |
|
|
687
|
+
| "Exile a card from your hand: …" (a cost) | `{ "exile_from_hand": {} }` (`count`, `card`: a card spec) |
|
|
688
|
+
| "activate only if this card is exiled" | `"zone": "exile"` on the activated ability |
|
|
689
|
+
| "Changeling (This object is every creature type.)" | `"keywords": ["changeling"]` |
|
|
690
|
+
|
|
691
|
+
A token copy is a token (the filter `"token": true` matches it). A copy of a permanent
|
|
692
|
+
spell becomes a token as it resolves (CR 608.3f). A spell copy that leaves the stack, or
|
|
693
|
+
a card copy outside the stack and battlefield, ceases to exist (CR 707.10a).
|
|
694
|
+
|
|
695
|
+
```json
|
|
696
|
+
{ "name": "Example Duplicant", "mana_cost": "{3}{U}", "types": ["creature"], "subtypes": ["shapeshifter"],
|
|
697
|
+
"power": 0, "toughness": 0,
|
|
698
|
+
"abilities": [{ "enter_as_copy": { "of": "creature", "except": { "legendary": false } } },
|
|
699
|
+
{ "activated": { "cost": [{ "mana": "{2}" }], "targets": [{ "creature": { "controller": "you" } }],
|
|
700
|
+
"effects": [{ "create_token_copy": { "of": { "target": 0 } } },
|
|
701
|
+
{ "grant": { "keywords": ["haste"], "to": { "result": 0 } } }] } }] }
|
|
702
|
+
```
|
|
703
|
+
|
|
704
|
+
### 4.15 Chosen colors, creature types, card types and words (CR 607.2d)
|
|
705
|
+
|
|
706
|
+
A choice is remembered on an object; "the chosen …" reads it. Made **as a permanent enters**
|
|
707
|
+
(an ability, CR 614.12; the enter_choice decision) it is remembered on the permanent, and the
|
|
708
|
+
permanent's other abilities read it. Made **while a spell or ability resolves** (an effect, an
|
|
709
|
+
effect_choice decision) it is remembered on that spell or ability, and its later effects read
|
|
710
|
+
it (and, failing that, its source's choice).
|
|
711
|
+
|
|
712
|
+
| Oracle words | Data |
|
|
713
|
+
| --- | --- |
|
|
714
|
+
| "As this enters, choose a color / creature type / card type" | ability `{ "choose_color": {} }`, `{ "choose_creature_type": {} }`, `{ "choose_card_type": {} }` |
|
|
715
|
+
| "As this enters, choose Khans or Dragons" (CR 607.2f) | ability `{ "choose_option": { "options": ["khans", "dragons"] } }` |
|
|
716
|
+
| "Choose a color / creature type / card type." / "Choose odd or even." (on resolution) | effect `{ "choose_color": {} }` (same names; `to`: who chooses, default you), `{ "choose_option": { "options": ["odd", "even"] } }` |
|
|
717
|
+
| "… of the chosen color / type / card type", "not of the chosen type" | filter `"colors": "chosen"`, `"subtypes": "chosen"`, `"types": "chosen"`, `"not": "chosen"`, `"not_subtypes": "chosen"` |
|
|
718
|
+
| "If odd was chosen …", "• Khans — …" | condition `{ "chosen": "odd" }` (in an effect's `if`, a static's `if`, a trigger's `if`) |
|
|
719
|
+
|
|
720
|
+
Nothing matches "the chosen type" while nothing was chosen. The options offered are the five
|
|
721
|
+
colors (CR 105.4), every creature type (CR 205.3m) and the card types (CR 205.2a).
|
|
722
|
+
|
|
723
|
+
```json
|
|
724
|
+
{ "name": "Example Kin Banner", "mana_cost": "{3}", "types": ["artifact"],
|
|
725
|
+
"abilities": [{ "choose_creature_type": {} },
|
|
726
|
+
{ "static": { "affects": { "each": { "creature": { "subtypes": "chosen" } }, "controller": "you" },
|
|
727
|
+
"pump": { "power": 1, "toughness": 1 } } },
|
|
728
|
+
{ "triggered": { "when": { "cast": { "spell": { "spell": { "subtypes": "chosen" } } } },
|
|
729
|
+
"effects": [{ "draw": { "amount": 1 } }] } }] }
|
|
730
|
+
```
|
|
731
|
+
|
|
732
|
+
```json
|
|
733
|
+
{ "name": "Example Kin Recall", "mana_cost": "{3}{B}", "types": ["sorcery"],
|
|
734
|
+
"abilities": [{ "spell": { "effects": [
|
|
735
|
+
{ "choose_creature_type": {} },
|
|
736
|
+
{ "return_to_battlefield": { "to": { "each": { "graveyard_card": { "controller": "you", "any_of": ["creature"],
|
|
737
|
+
"subtypes": "chosen" } } } } }] } }] }
|
|
738
|
+
```
|
|
739
|
+
|
|
740
|
+
### 4.16 Vehicles, type changes, exhaust and speed
|
|
741
|
+
|
|
742
|
+
**Vehicles and crew** (CR 301.7, 702.122). A Vehicle is an artifact with `"subtypes": ["vehicle"]`
|
|
743
|
+
and its printed `power`/`toughness` on the card: it has them only while it's a creature (CR 208.3).
|
|
744
|
+
"Crew N" is the ability `{ "crew": { "power": N } }`. Activating it asks a `choose_cost` decision per
|
|
745
|
+
creature to tap (other untapped creatures you control, summoning-sick ones too); once their total
|
|
746
|
+
power is N or more the player answers `done` (or taps more). It becomes an artifact creature until
|
|
747
|
+
end of turn.
|
|
748
|
+
|
|
749
|
+
```json
|
|
750
|
+
{ "name": "Example Hauler", "mana_cost": "{2}", "types": ["artifact"], "subtypes": ["vehicle"],
|
|
751
|
+
"power": 4, "toughness": 3, "keywords": ["trample"],
|
|
752
|
+
"abilities": [{ "crew": { "power": 2 } },
|
|
753
|
+
{ "triggered": { "when": { "becomes_crewed": {} },
|
|
754
|
+
"effects": [{ "gain_life": { "amount": 1 } }] } }] }
|
|
755
|
+
```
|
|
756
|
+
|
|
757
|
+
| Oracle words | Data |
|
|
758
|
+
| --- | --- |
|
|
759
|
+
| "Whenever this Vehicle becomes crewed" | `{ "becomes_crewed": {} }` (its crew ability resolved, CR 702.122e) |
|
|
760
|
+
| "Whenever this creature crews a Vehicle, that Vehicle gets +2/+0" | `{ "crews_vehicle": {} }` (`subject` like other triggers) with `"to": { "triggering": "object" }` |
|
|
761
|
+
| "During turns other than yours, this Vehicle is an artifact creature" | `{ "static": { "affects": "self", "if": { "not": { "your_turn": true } }, "types": ["artifact", "creature"] } }` |
|
|
762
|
+
| "Target Vehicle becomes an artifact creature until end of turn" | `{ "become": { "types": ["artifact", "creature"], "to": { "target": 0 } } }` |
|
|
763
|
+
|
|
764
|
+
**Type-changing effects** (CR 205.1b, 613.1d-e). `become` adds card `types`, `supertypes` and
|
|
765
|
+
`subtypes` "in addition to its other types", sets `creature_types` (replacing the others), `colors`
|
|
766
|
+
(layer 5), grants `keywords` and `abilities` and sets `base_power_toughness`, for `duration`
|
|
767
|
+
`end_of_turn` (default) or `indefinite`. A permanent that becomes a creature without a P/T setting
|
|
768
|
+
has its printed power and toughness, or 0/0. A manland, "{1}{R}: This land becomes a 3/1 red
|
|
769
|
+
Elemental creature with haste until end of turn. It's still a land.":
|
|
770
|
+
|
|
771
|
+
```json
|
|
772
|
+
{ "activated": { "cost": [{ "mana": "{1}{R}" }],
|
|
773
|
+
"effects": [{ "become": { "types": ["creature"], "creature_types": ["elemental"], "colors": ["R"],
|
|
774
|
+
"keywords": ["haste"],
|
|
775
|
+
"base_power_toughness": { "power": 3, "toughness": 1 }, "to": "self" } }] } }
|
|
776
|
+
```
|
|
777
|
+
|
|
778
|
+
"Target creature becomes an artifact in addition to its other types" is `"types": ["artifact"]`;
|
|
779
|
+
"becomes a legendary Spider Hero in addition to its other types" is `"supertypes": ["legendary"],
|
|
780
|
+
"subtypes": ["spider", "hero"]`. Static abilities take `types` the same way.
|
|
781
|
+
|
|
782
|
+
**Exhaust** (CR 702.177). "Exhaust — {2}{G}: …" is an activated ability with `"exhaust": true`: it
|
|
783
|
+
can be activated only once for as long as the permanent stays. "Whenever you activate an exhaust
|
|
784
|
+
ability" is `{ "activates_ability": { "exhaust": true } }`.
|
|
785
|
+
|
|
786
|
+
```json
|
|
787
|
+
{ "activated": { "exhaust": true, "cost": [{ "mana": "{2}{G}" }],
|
|
788
|
+
"effects": [{ "add_counters": { "counter": "+1/+1", "amount": 2, "to": "self" } }] } }
|
|
789
|
+
```
|
|
790
|
+
|
|
791
|
+
**Start your engines! and max speed** (CR 702.178, 702.179). `"keywords": ["start_your_engines"]`
|
|
792
|
+
gives its controller speed 1 if they have none; the speed then rises by 1 (to at most 4) once on
|
|
793
|
+
each of their turns when an opponent loses life, as a triggered ability with no source. "Max speed —
|
|
794
|
+
[ability]" is `{ "max_speed": { "keywords": [...], "abilities": [...] } }`: any kind of ability, which
|
|
795
|
+
the object has only while its controller's speed is 4 (a card in the graveyard: its owner's).
|
|
796
|
+
"Your speed" is the amount `{ "speed": "you" }`; "as long as you have max speed" is the condition
|
|
797
|
+
`{ "max_speed": true }`.
|
|
798
|
+
|
|
799
|
+
```json
|
|
800
|
+
{ "name": "Example Racer", "mana_cost": "{1}{R}", "types": ["creature"], "subtypes": ["goblin", "pilot"],
|
|
801
|
+
"power": 2, "toughness": 2, "keywords": ["start_your_engines"],
|
|
802
|
+
"abilities": [{ "max_speed": { "keywords": ["double_strike"] } },
|
|
803
|
+
{ "max_speed": { "abilities": [
|
|
804
|
+
{ "activated": { "cost": [{ "mana": "{2}" }, "exile_self_from_graveyard"],
|
|
805
|
+
"effects": [{ "draw": { "amount": 1 } }] } }] } }] }
|
|
806
|
+
```
|
|
807
|
+
|
|
808
|
+
### 4.17 Cards with faces: double-faced, Adventure, Omen and prepare cards
|
|
809
|
+
|
|
810
|
+
A card with more than one set of characteristics has `layout` and `faces` instead of the
|
|
811
|
+
top-level fields of section 2. Each face is a complete card block (its own `name`,
|
|
812
|
+
`mana_cost`, `types`, `abilities`, …), front face first. A top-level `name` is optional;
|
|
813
|
+
if given, it must be the faces' names joined by `" // "`. The card id comes from the front
|
|
814
|
+
face's name (`"Alfa // Bravo"` → `alfa`), so decks may name it either way.
|
|
815
|
+
|
|
816
|
+
| `layout` | Cards | Rules |
|
|
817
|
+
| --- | --- | --- |
|
|
818
|
+
| `transform` | nonmodal double-faced cards: cast and played front face up; they transform or enter transformed | CR 712.2, 701.27 |
|
|
819
|
+
| `modal` | modal double-faced cards: choose the face to cast or play (a modal card can also transform) | CR 712.3, 712.8b, 712.9 |
|
|
820
|
+
| `adventure` | adventurer cards: `faces[1]` is the Adventure (instant or sorcery with the subtype `adventure`) | CR 716 |
|
|
821
|
+
| `omen` | omen cards: `faces[1]` is the Omen (instant or sorcery with the subtype `omen`) | CR 721 |
|
|
822
|
+
| `prepare` | preparation cards: `faces[1]` is the prepare spell (an instant or sorcery) | CR 723 |
|
|
823
|
+
|
|
824
|
+
Outside the battlefield and stack a card has only its front face's characteristics
|
|
825
|
+
(CR 712.6a, 716.4, 721.4); a game object shows the face that's up by its `card`: the card
|
|
826
|
+
id for the front face, `"<card id>/1"` while the back face (or the Adventure or Omen on
|
|
827
|
+
the stack) is up. A copy of a permanent copies the face that's up (CR 707.8).
|
|
828
|
+
|
|
829
|
+
| Oracle words | Data |
|
|
830
|
+
| --- | --- |
|
|
831
|
+
| "Transform this creature." | `{ "transform": {} }` (`to`: any selector; default `"self"`) |
|
|
832
|
+
| "Whenever this creature transforms into [this face]" | `{ "triggered": { "when": { "transforms": {} }, … } }` on that face |
|
|
833
|
+
| "When this creature enters or transforms into [this face]" | `"when": { "any_of": [{ "enters": {} }, { "transforms": {} }] }` |
|
|
834
|
+
| "exile it, then return it to the battlefield transformed" | `exile`, then `{ "return_to_battlefield": { "to": { "result": 0 }, "transformed": true } }` |
|
|
835
|
+
| "if this creature is transformed" / "transformed permanents you control" | filter `{ "creature": { "transformed": true } }` |
|
|
836
|
+
| "This creature enters prepared." | the ability `"enters_prepared"` |
|
|
837
|
+
| "… this creature becomes prepared." / "becomes unprepared" | `{ "prepare": {} }` / `{ "unprepare": {} }` |
|
|
838
|
+
| "if this creature isn't prepared" | `"if": { "source": { "creature": { "prepared": false } } }` |
|
|
839
|
+
|
|
840
|
+
Casting another face is a priority option with `face`: `{type: "cast", object: id,
|
|
841
|
+
face: 1}` casts a modal card's back face, an Adventure or an Omen from the hand, and
|
|
842
|
+
`{type: "play_land", object: id, face: 1}` plays a modal card's land face. An Adventure
|
|
843
|
+
that resolves is exiled, and its owner may play the card from exile (not as an Adventure);
|
|
844
|
+
an Omen that resolves is shuffled into its owner's library. A permanent that becomes
|
|
845
|
+
prepared gives its controller a copy of its prepare spell in exile, which they may cast
|
|
846
|
+
until the permanent stops being prepared; casting it unprepares the permanent.
|
|
847
|
+
|
|
848
|
+
```json
|
|
849
|
+
{ "name": "Example Squire // Example Reaver", "layout": "transform", "faces": [
|
|
850
|
+
{ "name": "Example Squire", "mana_cost": "{1}{W}", "types": ["creature"], "power": 2, "toughness": 2,
|
|
851
|
+
"abilities": [{ "activated": { "cost": [{ "mana": "{3}" }], "timing": "sorcery", "effects": [{ "transform": {} }] } }] },
|
|
852
|
+
{ "name": "Example Reaver", "colors": ["W"], "types": ["creature"], "keywords": ["lifelink"], "power": 3, "toughness": 3,
|
|
853
|
+
"abilities": [{ "triggered": { "when": { "transforms": {} }, "effects": [{ "draw": { "amount": 1 } }] } }] }
|
|
854
|
+
] }
|
|
855
|
+
```
|
|
856
|
+
|
|
857
|
+
```json
|
|
858
|
+
{ "name": "Example Knight // Example Errand", "layout": "adventure", "faces": [
|
|
859
|
+
{ "name": "Example Knight", "mana_cost": "{2}{G}", "types": ["creature"], "power": 3, "toughness": 2 },
|
|
860
|
+
{ "name": "Example Errand", "mana_cost": "{1}{G}", "types": ["sorcery"], "subtypes": ["adventure"],
|
|
861
|
+
"abilities": [{ "spell": { "effects": [{ "gain_life": { "amount": 3 } }] } }] }
|
|
862
|
+
] }
|
|
863
|
+
```
|
|
864
|
+
|
|
865
|
+
```json
|
|
866
|
+
{ "name": "Example Scholar // Example Notes", "layout": "prepare", "faces": [
|
|
867
|
+
{ "name": "Example Scholar", "mana_cost": "{1}{U}", "types": ["creature"], "power": 1, "toughness": 3,
|
|
868
|
+
"abilities": ["enters_prepared"] },
|
|
869
|
+
{ "name": "Example Notes", "mana_cost": "{U}", "types": ["sorcery"],
|
|
870
|
+
"abilities": [{ "spell": { "effects": [{ "draw": { "amount": 1 } }] } }] }
|
|
871
|
+
] }
|
|
872
|
+
```
|
|
873
|
+
|
|
874
|
+
Not yet: casting another face from outside the hand, casting a card transformed, the mana
|
|
875
|
+
value of a back face (CR 202.3b), day/night (CR 702.145), meld (CR 713), convert
|
|
876
|
+
(CR 701.28) and an "as this enters" choice on a modal card's land face.
|
|
877
|
+
|
|
878
|
+
### 4.18 Mana spent to cast a spell (converge)
|
|
879
|
+
|
|
880
|
+
The mana paid for a spell is recorded by type as it is paid (CR 601.2h), and the permanent the
|
|
881
|
+
spell becomes keeps it (CR 400.7d). The amount `mana_spent` reads it:
|
|
882
|
+
|
|
883
|
+
| Oracle words | Data |
|
|
884
|
+
| --- | --- |
|
|
885
|
+
| "the amount of mana spent to cast this spell / this creature" | `{ "mana_spent": {} }` |
|
|
886
|
+
| converge: "the number of colors of mana spent to cast it" | `{ "mana_spent": { "colors": true } }` (colorless isn't a color) |
|
|
887
|
+
| "if {W}{W} was spent to cast it" | `{ "amount": { "mana_spent": { "color": "W" } }, "at_least": 2 }` |
|
|
888
|
+
| "whenever you cast a spell, if four or more mana was spent to cast it" | trigger `if`: `{ "amount": { "mana_spent": { "of": { "triggering": "object" } } }, "at_least": 4 }` |
|
|
889
|
+
| "the amount of mana spent to cast target spell" | `{ "mana_spent": { "of": { "target": 0 } } }` |
|
|
890
|
+
| "enters with a +1/+1 counter for each color of mana spent to cast it" | `{ "enters_with_counters": { "counters": { "+1/+1": { "mana_spent": { "colors": true } } } } }` |
|
|
891
|
+
|
|
892
|
+
A trigger's `if` is checked as it triggers with the event it's about (CR 603.4), so "that
|
|
893
|
+
spell" works there. Nothing was spent on a permanent that wasn't cast, and a copy of a spell
|
|
894
|
+
had no mana spent on it (CR 707.10).
|
|
895
|
+
|
|
896
|
+
```json
|
|
897
|
+
{ "name": "Example Prism Archaic", "mana_cost": "{4}{G}", "types": ["creature"], "subtypes": ["avatar"],
|
|
898
|
+
"power": 1, "toughness": 1,
|
|
899
|
+
"abilities": [{ "enters_with_counters": { "counters": { "+1/+1": { "mana_spent": { "colors": true } } } } },
|
|
900
|
+
{ "triggered": { "when": { "enters": {} }, "if": { "amount": { "mana_spent": { "color": "G" } }, "at_least": 2 },
|
|
901
|
+
"effects": [{ "draw": { "amount": 1 } }] } }] }
|
|
902
|
+
```
|
|
903
|
+
|
|
904
|
+
### 4.19 Keyword actions and keyword abilities of recent sets
|
|
905
|
+
|
|
906
|
+
Keyword *abilities* without a number go in `keywords`; those with a number are abilities
|
|
907
|
+
with an `amount` (any amount, e.g. `{ "power": "self" }` for "firebending X, where X is its
|
|
908
|
+
power"). Keyword *actions* are effects.
|
|
909
|
+
|
|
910
|
+
| Oracle text | Data |
|
|
911
|
+
| --- | --- |
|
|
912
|
+
| "Prowess" (CR 702.108) | `"keywords": ["prowess"]` (granted by `grant` too) |
|
|
913
|
+
| "Job select" on an Equipment (CR 702.182) | `"keywords": ["job_select"]` |
|
|
914
|
+
| "Mobilize 2" (CR 702.181) | ability `{ "mobilize": { "amount": 2 } }` |
|
|
915
|
+
| "Firebending 2" (CR 702.189) | ability `{ "firebending": { "amount": 2 } }` |
|
|
916
|
+
| "Kindred Sorcery — Goblin" (CR 308) | `"types": ["kindred", "sorcery"], "subtypes": ["goblin"]` |
|
|
917
|
+
| "As an additional cost to cast this spell, blight 1" (CR 701.68) | `{ "additional_cost": { "cost": [{ "blight": { "amount": 1 } }] } }`; also an activated ability's `cost` |
|
|
918
|
+
| "you may blight 2. If you do, draw a card" | `{ "may": { "effects": [{ "blight": { "amount": 2 } }], "then": [{ "draw": { "amount": 1 } }] } }` |
|
|
919
|
+
| "each opponent blights 1" | `{ "blight": { "amount": 1, "to": "each_opponent" } }` (the blighted creatures are its results) |
|
|
920
|
+
| "it connives" / "target creature you control connives 2" (CR 701.50) | `{ "connive": {} }` / `{ "connive": { "amount": 2, "to": { "target": 0 } } }` |
|
|
921
|
+
| "recruit" (CR 701.70) | `{ "recruit": {} }` |
|
|
922
|
+
| "amass Orcs 2" (CR 701.47) | `{ "amass": { "subtype": "orc", "amount": 2 } }` (the amassed Army is its result: "attach this Equipment to the amassed Army" is `attach` to `{ "result": n }`) |
|
|
923
|
+
| "empower Jace 2" (CR 701.71) | `{ "empower_jace": { "amount": 2 } }` |
|
|
924
|
+
| "it endures 2" (CR 701.63) | `{ "endure": { "amount": 2 } }` (any amount, see 4.24) |
|
|
925
|
+
| "create two 1/1 tokens tapped and attacking" | `create_token` with `"tapped": true, "attacking": true` |
|
|
926
|
+
| "add {R}{R}; until end of combat, you don't lose this mana" | `{ "add_mana": { "produce": "{R}{R}", "until": "end_of_combat" } }` |
|
|
927
|
+
| "earthbend 2" (CR 701.66) | a target `{ "land": { "controller": "you" } }` and `{ "earthbend": { "amount": 2, "to": { "target": 0 } } }` (the land is its result: "that land becomes an Island" is `become` with `{ "result": n }`) |
|
|
928
|
+
| "Waterbend {3}: …" / "As an additional cost to cast this spell, waterbend {X}" (CR 701.67) | the cost `{ "waterbend": "{3}" }` in an activated ability's `cost`, an `additional_cost` (optional or not) or `kicker`; while paying, `{type: "waterbend", object: id}` taps an artifact or creature for {1} of it |
|
|
929
|
+
| "you may waterbend {4} as an additional cost … if this spell's additional cost was paid" | `{ "additional_cost": { "cost": [{ "waterbend": "{4}" }], "optional": true } }` and the condition `{ "cast_with": "additional_cost" }` (4.24) |
|
|
930
|
+
| "discard a card unless you waterbend {2}" / "Ward—Waterbend {4}" | `{ "pay": { "waterbend": "{2}", "otherwise": [{ "discard": { "amount": 1, "to": "you" } }] } }` / `{ "ward": { "waterbend": "{4}" } }` (also `counter_unless_pays`) |
|
|
931
|
+
| "airbend target creature or spell" (CR 701.65) | `{ "airbend": { "to": { "target": 0 } } }` (the owner may cast each exiled card for {2} while it stays exiled) |
|
|
932
|
+
| "Whenever you waterbend, earthbend, firebend, or airbend" | `{ "any_of": [{ "waterbends": {} }, { "earthbends": {} }, { "firebends": {} }, { "airbends": {} }] }` |
|
|
933
|
+
| "Saddle 2" (CR 702.171) / "becomes saddled until end of turn" | ability `{ "saddle": { "power": 2 } }` / effect `{ "become_saddled": { "to": … } }` |
|
|
934
|
+
| "Whenever this creature attacks while saddled" | `"when": { "attacks": {} }, "while": { "source": { "creature": { "saddled": true } } }` (filters take `"saddled"`) |
|
|
935
|
+
| "Whenever this creature becomes saddled" / "saddles a Mount" | `{ "becomes_saddled": {} }` / `{ "saddles_mount": {} }` (`{ "triggering": "object" }` is the Mount) |
|
|
936
|
+
| "{W}, {T}, Exert this creature: …" (CR 701.43) | the cost `"exert"`: it doesn't untap during your next untap step |
|
|
937
|
+
| "Decayed" (CR 702.147), "Wither" (CR 702.80) | `"keywords": ["decayed"]` (a `decayed` counter gives it too), `"keywords": ["wither"]` |
|
|
938
|
+
|
|
939
|
+
```json
|
|
940
|
+
{ "name": "Example Ember Captain", "mana_cost": "{2}{R}", "types": ["creature"], "subtypes": ["human", "soldier"],
|
|
941
|
+
"power": 2, "toughness": 2, "keywords": ["prowess"],
|
|
942
|
+
"abilities": [{ "mobilize": { "amount": 1 } }, { "firebending": { "amount": { "power": "self" } } },
|
|
943
|
+
{ "triggered": { "when": { "enters": {} }, "effects": [{ "connive": {} }] } }] }
|
|
944
|
+
```
|
|
945
|
+
|
|
946
|
+
```json
|
|
947
|
+
{ "name": "Example Stone Monk", "mana_cost": "{2}{G}", "types": ["creature"], "subtypes": ["human", "monk", "mount"],
|
|
948
|
+
"power": 2, "toughness": 2,
|
|
949
|
+
"abilities": [{ "triggered": { "when": { "enters": {} }, "targets": [{ "land": { "controller": "you" } }],
|
|
950
|
+
"effects": [{ "earthbend": { "amount": 2, "to": { "target": 0 } } }] } },
|
|
951
|
+
{ "activated": { "cost": [{ "waterbend": "{3}" }], "effects": [{ "draw": { "amount": 1 } }] } },
|
|
952
|
+
{ "saddle": { "power": 1 } }] }
|
|
953
|
+
```
|
|
954
|
+
|
|
955
|
+
### 4.20 Set mechanics: power-up, teamwork, tiered, Class cards, increment, extort, riot, storm, cascade, boast, storied, colors among, domain
|
|
956
|
+
|
|
957
|
+
| Oracle text | Data |
|
|
958
|
+
| --- | --- |
|
|
959
|
+
| "Power-up — {5}{U}: …" (CR 702.193) | `{ "activated": { "power_up": true, "cost": [{ "mana": "{5}{U}" }], "effects": [...] } }`: activate only once; the turn the permanent entered, the cost is reduced by its mana cost (colored mana by the same color, the excess and generic by generic, CR 118.7) |
|
|
960
|
+
| "Boast — {1}{R}: …" (CR 702.142) | `"boast": true` on the activated ability: only if this creature attacked this turn, once each turn |
|
|
961
|
+
| "Teamwork 4" (CR 702.194) | ability `{ "teamwork": { "power": 4 } }`: announced like kicker (`choose_kicker`), then one `choose_cost` per creature tapped until their total power is 4 or more |
|
|
962
|
+
| "if this spell was cast using teamwork" | condition `{ "cast_with": "teamwork" }`; "choose both instead" is `"choose_if": { "if": { "cast_with": "teamwork" }, "choose": 2 }`; targets only with teamwork are `kicked_targets` |
|
|
963
|
+
| "Increment" (CR 702.191) | `"keywords": ["increment"]` |
|
|
964
|
+
| "Riot" (CR 702.136) | `"keywords": ["riot"]`: an `enter_choice` of `{type: "option", option: "counter"}` (an additional +1/+1 counter) or `"haste"` (it can't have another "as this enters" choice) |
|
|
965
|
+
| "Storm" (CR 702.40), "Cascade" (CR 702.85) | `"keywords": ["storm"]`, `"keywords": ["cascade"]`; a cast trigger's "that many" (`{ "triggering": "amount" }`) is the number of spells cast before that spell this turn |
|
|
966
|
+
| "Extort" (CR 702.101) | `"keywords": ["extort"]` (a `pay` {W/B} choice as it resolves) |
|
|
967
|
+
| "the number of colors among permanents you control" (CR 105.2) | amount `{ "distinct_colors": { "each": "permanent", "controller": "you" } }` |
|
|
968
|
+
| domain: "the number of basic land types among lands you control" (CR 305.6) | amount `{ "distinct_land_types": { "each": "land", "controller": "you" } }` |
|
|
969
|
+
| "Tiered" (CR 702.183): "• Fira — {2} — …" | a modal `spell` with `"choose": 1` whose modes each have a `cost`: `{ "cost": "{2}", "effects": [...] }`; the chosen mode's cost is paid in addition (modes the player can't pay for aren't offered) |
|
|
970
|
+
| Class cards (CR 717): "{1}{R}: Level 2 — [abilities]" | `"subtypes": ["class"]` and one ability per level bar: `{ "class_level": { "level": 2, "cost": "{1}{R}", "abilities": [...], "keywords": [...] } }`; the abilities above the first bar are ordinary abilities. Gaining a level is activated as a sorcery, only from the level below; a permanent without a level is level 1 |
|
|
971
|
+
| "When this Class becomes level 3" | trigger `{ "becomes_level": { "level": 3 } }` (listed in that level's `abilities`) |
|
|
972
|
+
| "Storied" (CR 702.195) | `"keywords": ["storied"]`: once you control it and three or more artifacts, Sagas and/or legendary permanents, you have an enduring story for the rest of the game (the player view's `designations`) |
|
|
973
|
+
| "as long as you have an enduring story" | condition `{ "designation": "enduring_story" }` (a static's or trigger's `if`, an activated ability's `only_if`) |
|
|
974
|
+
|
|
975
|
+
```json
|
|
976
|
+
{ "name": "Example Gamma Brawler", "mana_cost": "{1}{W}", "types": ["creature"], "power": 2, "toughness": 1,
|
|
977
|
+
"keywords": ["increment"],
|
|
978
|
+
"abilities": [{ "activated": { "power_up": true, "cost": [{ "mana": "{4}{W}" }],
|
|
979
|
+
"effects": [{ "add_counters": { "counter": "+1/+1", "amount": 2, "to": "self" } }] } }] }
|
|
980
|
+
```
|
|
981
|
+
|
|
982
|
+
### 4.21 Rules-changing effects: losing abilities, restrictions, protection
|
|
983
|
+
|
|
984
|
+
**Losing all abilities** (CR 613.1f). `"loses_abilities": true` on a `become` effect or a `static`
|
|
985
|
+
ability. Layer 6 applies in timestamp order (CR 613.7): the permanent loses what it had, and
|
|
986
|
+
keeps abilities granted later and keyword counters put on it later. A permanent that loses its
|
|
987
|
+
abilities loses its static abilities' effects on others too (CR 613.8a), its characteristic-defining
|
|
988
|
+
power and toughness, and its "when this dies" abilities. A `static` can also set `colors` (layer 5)
|
|
989
|
+
and `base_power_toughness` (layer 7b); its `abilities` may be activated abilities, as `become`'s may.
|
|
990
|
+
|
|
991
|
+
| Oracle words | Data |
|
|
992
|
+
| --- | --- |
|
|
993
|
+
| "Target creature loses all abilities until end of turn" | `{ "become": { "loses_abilities": true, "to": { "target": 0 } } }` |
|
|
994
|
+
| "… loses all abilities for as long as this creature remains on the battlefield" | `become` with `"duration": "while_source_on_battlefield"` (does nothing if this has already left, CR 611.2b) |
|
|
995
|
+
| "Enchanted creature loses all abilities and is a black Skeleton with base power and toughness 1/1" | `{ "static": { "affects": "attached", "loses_abilities": true, "colors": ["B"], "creature_types": ["skeleton"], "base_power_toughness": { "power": 1, "toughness": 1 } } }` |
|
|
996
|
+
| "Other creatures you control have '{1}: This creature gets +1/+0 until end of turn'" | `static` with `"abilities": [{ "activated": … }]` (`self` is the creature that has it) |
|
|
997
|
+
| "This creature is the chosen type in addition to its other types" | `{ "choose_creature_type": {} }` and `{ "static": { "affects": "self", "subtypes": "chosen" } }` |
|
|
998
|
+
| "As this land enters, choose a basic land type. This land is the chosen type." | `{ "choose_basic_land_type": {} }` and the same `static` (a land with a basic land type has its mana ability, CR 305.6) |
|
|
999
|
+
|
|
1000
|
+
```json
|
|
1001
|
+
{ "name": "Example Fishbowl", "mana_cost": "{1}{U}", "types": ["enchantment"], "subtypes": ["aura"],
|
|
1002
|
+
"keywords": ["flash"],
|
|
1003
|
+
"abilities": [{ "enchant": "creature" },
|
|
1004
|
+
{ "static": { "affects": "attached", "loses_abilities": true, "colors": ["U"],
|
|
1005
|
+
"creature_types": ["fish"], "base_power_toughness": { "power": 1, "toughness": 1 } } }] }
|
|
1006
|
+
```
|
|
1007
|
+
|
|
1008
|
+
**Restrictions on players and the game** (CR 101.2: a "can't" beats any "can"). They are
|
|
1009
|
+
`player_rule` abilities (static, with an optional `if` condition seen from the source) or
|
|
1010
|
+
`player_rule` effects (for a duration), with `affects` / `to` naming the players:
|
|
1011
|
+
|
|
1012
|
+
| Oracle words | Data |
|
|
1013
|
+
| --- | --- |
|
|
1014
|
+
| "Your opponents can't cast spells during your turn" | `{ "player_rule": { "rule": "cant_cast", "affects": "opponents", "if": { "your_turn": true } } }` |
|
|
1015
|
+
| "Players can't cast spells this turn" | effect `{ "player_rule": { "rule": "cant_cast", "to": { "each": "player" } } }` |
|
|
1016
|
+
| "… can't cast spells from anywhere other than their hands" | rule `cant_cast_except_from_hand` |
|
|
1017
|
+
| "Creature spells you control can't be countered" | `{ "player_rule": { "rule": "spells_cant_be_countered", "spells": { "types": ["creature"] } } }` (`spells`: a card spec; also on `cant_cast`) |
|
|
1018
|
+
| "Players can't gain life" | `{ "player_rule": { "rule": "cant_gain_life", "affects": "each" } }` |
|
|
1019
|
+
| "Activated abilities of creatures can't be activated unless they're mana abilities" | `{ "player_rule": { "rule": "cant_activate", "affects": "each", "filter": "creature" } }` |
|
|
1020
|
+
| "Damage can't be prevented (this turn)" / "Combat damage can't be prevented" | rule `damage_cant_be_prevented` / `combat_damage_cant_be_prevented` (game-wide, CR 615.12) |
|
|
1021
|
+
|
|
1022
|
+
```json
|
|
1023
|
+
{ "name": "Example Hush Captain", "mana_cost": "{1}{W}", "types": ["creature"], "subtypes": ["human"],
|
|
1024
|
+
"power": 2, "toughness": 2,
|
|
1025
|
+
"abilities": [{ "player_rule": { "rule": "cant_cast", "affects": "opponents", "if": { "your_turn": true } } },
|
|
1026
|
+
{ "player_rule": { "rule": "cant_gain_life", "affects": "opponents" } }] }
|
|
1027
|
+
```
|
|
1028
|
+
|
|
1029
|
+
**Life totals, exchanges and player counters** (CR 119.5, 701.12, 122.1).
|
|
1030
|
+
|
|
1031
|
+
| Oracle words | Data |
|
|
1032
|
+
| --- | --- |
|
|
1033
|
+
| "Your life total becomes 10" | `{ "set_life": { "amount": 10 } }` (gains or loses the difference) |
|
|
1034
|
+
| "Exchange life totals with target opponent. If you lost life this way, draw that many cards" | `{ "exchange_life_totals": { "with": { "target": 0 } } }`, then `{ "draw": { "amount": { "this_way": 0 } } }` |
|
|
1035
|
+
| "Gain control of target creature for as long as this creature remains on the battlefield" | `{ "gain_control": { "to": { "target": 0 }, "duration": "while_source_on_battlefield" } }` (also `while_source_tapped`; control goes back as it ends, CR 611.2b) |
|
|
1036
|
+
| "Exchange control of target creature you control and target creature an opponent controls" | `{ "exchange_control": { "to": { "target": 0 }, "with": { "target": 1 } } }` |
|
|
1037
|
+
| "that player gets two poison counters" / "you get an experience counter" | `{ "add_counters": { "counter": "poison", "amount": 2, "to": { "triggering": "player" } } }` / `"counter": "experience", "to": "you"` |
|
|
1038
|
+
| "the number of experience counters you have" | amount `{ "counters": { "of": "you", "kind": "experience" } }` |
|
|
1039
|
+
| "Target opponent skips their next turn" | `{ "skip_turns": { "amount": 1, "to": { "target": 0 } } }` (CR 614.10; skips add up) |
|
|
1040
|
+
|
|
1041
|
+
A player with ten or more poison counters loses the game (CR 704.5c). An exchange that can't be
|
|
1042
|
+
completed doesn't happen at all (CR 701.12a).
|
|
1043
|
+
|
|
1044
|
+
```json
|
|
1045
|
+
{ "name": "Example Venom Adder", "mana_cost": "{1}{G}", "types": ["creature"], "subtypes": ["snake"],
|
|
1046
|
+
"power": 1, "toughness": 2, "keywords": ["deathtouch"],
|
|
1047
|
+
"abilities": [{ "triggered": { "when": { "deals_damage": { "combat": true, "to": "player" } },
|
|
1048
|
+
"effects": [{ "add_counters": { "counter": "poison", "amount": 2,
|
|
1049
|
+
"to": { "triggering": "player" } } }] } }] }
|
|
1050
|
+
```
|
|
1051
|
+
|
|
1052
|
+
**Emblems** (CR 114). "You get an emblem with '…'" is `create_emblem` with the emblem's
|
|
1053
|
+
`abilities` (triggered, static or player_rule). The emblem goes to the command zone for the rest
|
|
1054
|
+
of the game and its abilities work from there; "this emblem" is the source of what they do. The
|
|
1055
|
+
game view lists emblems under `command`.
|
|
1056
|
+
|
|
1057
|
+
```json
|
|
1058
|
+
{ "loyalty": { "cost": -7, "effects": [
|
|
1059
|
+
{ "create_emblem": { "abilities": [
|
|
1060
|
+
{ "triggered": { "when": { "cast": {} }, "targets": ["any_target"],
|
|
1061
|
+
"effects": [{ "deal_damage": { "amount": 5, "to": { "target": 0 } } }] } }] } }] } }
|
|
1062
|
+
```
|
|
1063
|
+
|
|
1064
|
+
**Hexproof from and protection from a quality** (CR 702.11d, 702.16). Abilities whose value is a
|
|
1065
|
+
card spec of the quality, matched against the spell, or the source of the ability or damage, as it
|
|
1066
|
+
is now: `{ "colors": ["B"] }` (any of those colors), `{ "types": ["instant"] }`,
|
|
1067
|
+
`{ "color_count": { "equal": 1 } }` (monocolored; `at_least: 2` multicolored). "From A and from B"
|
|
1068
|
+
is two abilities. Protection also prevents damage from such sources, stops such creatures from
|
|
1069
|
+
blocking it and such Auras and Equipment from staying attached (CR 704.5m-n). `"protection_from":
|
|
1070
|
+
"everything"` is protection from everything. Both may be granted by `static` and `become` `abilities`.
|
|
1071
|
+
|
|
1072
|
+
```json
|
|
1073
|
+
{ "name": "Example Ward Knight", "mana_cost": "{1}{W}", "types": ["creature"], "subtypes": ["human", "knight"],
|
|
1074
|
+
"power": 2, "toughness": 2,
|
|
1075
|
+
"abilities": [{ "hexproof_from": { "colors": ["B"] } }, { "protection_from": { "colors": ["R"] } }] }
|
|
1076
|
+
```
|
|
1077
|
+
|
|
1078
|
+
### 4.22 Damage and combat rules
|
|
1079
|
+
|
|
1080
|
+
A damage replacement or prevention ability names the damage it watches with `by` (sources) and `to`
|
|
1081
|
+
(recipients), both optional: `"self"`, `"attached"` or `{ "each": filter, "controller": "you" | "opponent" |
|
|
1082
|
+
"any", "other": bool }`. `each` is optional and players match too, so `{ "controller": "opponent" }` is "an
|
|
1083
|
+
opponent or a permanent an opponent controls". `combat` is `"combat"` or `"noncombat"`.
|
|
1084
|
+
|
|
1085
|
+
| Oracle text | Data |
|
|
1086
|
+
| --- | --- |
|
|
1087
|
+
| "If a source you control would deal damage to an opponent or a permanent an opponent controls, it deals double that damage instead" | `{ "damage_replacement": { "by": { "controller": "you" }, "to": { "controller": "opponent" }, "multiply": 2 } }` |
|
|
1088
|
+
| "... noncombat damage ..., it deals that much damage plus 1 instead" | same with `"combat": "noncombat", "plus": 1` (`plus` is an amount) |
|
|
1089
|
+
| "All damage that would be dealt to you and other permanents you control is dealt to this creature instead" | `{ "damage_replacement": { "to": { "controller": "you", "other": true }, "redirect": "self" } }` |
|
|
1090
|
+
| "Prevent all combat damage that would be dealt to and dealt by this creature" | two abilities: `{ "prevent_damage": { "to": "self", "combat": "combat" } }` and the same with `"by": "self"` |
|
|
1091
|
+
| "Damage can't be prevented" / "... this turn" | ability `"damage_unpreventable"` / effect `"damage_unpreventable"` |
|
|
1092
|
+
| "Prevent the next 3 damage that would be dealt to any target this turn" | effect `{ "prevent_damage": { "to": { "target": 0 }, "next": 3 } }` (`by` instead of or with `to`: "dealt by target creature") |
|
|
1093
|
+
| "Whenever this creature is dealt damage" / "Whenever an opponent is dealt noncombat damage" | trigger `{ "dealt_damage": {} }` / `{ "dealt_damage": { "player": "opponent", "noncombat": true } }` ("that much" is `{ "triggering": "amount" }`) |
|
|
1094
|
+
| "Whenever a source you control deals noncombat damage to an opponent" | trigger `{ "deals_damage": { "subject": { "each": "permanent", "controller": "you" }, "noncombat": true, "to": "opponent" } }` |
|
|
1095
|
+
| "if an opponent was dealt noncombat damage this turn" | `{ "amount": { "noncombat_damage_this_turn": "each_opponent" }, "at_least": 1 }` |
|
|
1096
|
+
| "Whenever this creature blocks a creature with flying" | trigger `{ "blocks": { "blocking": { "creature": { "keywords": ["flying"] } } } }` |
|
|
1097
|
+
| "If excess damage was dealt this way" / "equal to the excess damage" | `{ "this_way": n }` of the `deal_damage` effect n |
|
|
1098
|
+
| "This creature must be blocked if able" | `"keywords": ["must_be_blocked"]` |
|
|
1099
|
+
| "assigns combat damage equal to its toughness rather than its power" (when greater) | `"keywords": ["assigns_damage_by_toughness"]`, or a `static`/`grant` handing it out |
|
|
1100
|
+
| "After this phase, there is an additional combat phase [followed by an additional main phase]" | `{ "additional_phases": { "phases": ["combat", "main"] } }`; "if it's the first combat phase of the turn" is `{ "first_combat_phase": true }` |
|
|
1101
|
+
| "If a creature an opponent controls would die, exile it instead" | `{ "exile_if_dies": { "affects": { "each": "creature", "controller": "opponent" } } }` |
|
|
1102
|
+
| "If one or more tokens would be created under your control, twice that many ... instead" | `"double_tokens"` |
|
|
1103
|
+
|
|
1104
|
+
```json
|
|
1105
|
+
{ "name": "Example Pyre Warden", "mana_cost": "{2}{R}{W}", "types": ["creature"], "subtypes": ["human", "wizard"],
|
|
1106
|
+
"power": 2, "toughness": 4,
|
|
1107
|
+
"abilities": [{ "damage_replacement": { "by": { "controller": "you" }, "to": { "controller": "opponent" },
|
|
1108
|
+
"combat": "noncombat", "plus": 1 } },
|
|
1109
|
+
{ "prevent_damage": { "to": { "each": "creature", "controller": "you", "other": true }, "combat": "noncombat" } },
|
|
1110
|
+
{ "triggered": { "when": { "dealt_damage": { "player": "opponent", "noncombat": true } },
|
|
1111
|
+
"effects": [{ "gain_life": { "amount": 1 } }] } }] }
|
|
1112
|
+
```
|
|
1113
|
+
|
|
1114
|
+
### 4.23 Spell targets, cast zones, copying abilities, randomness and proliferate
|
|
1115
|
+
|
|
1116
|
+
| Oracle text | Data |
|
|
1117
|
+
| --- | --- |
|
|
1118
|
+
| "Whenever you cast a spell that targets a creature you control" (CR 603.2, 115.1) | `{ "cast": { "targets": { "creature": { "controller": "you" } } } }` (any target filter, matched relative to the source) |
|
|
1119
|
+
| "Whenever you cast a spell that targets this creature" | `{ "cast": { "targets": "self" } }` |
|
|
1120
|
+
| "Whenever you cast an instant or sorcery spell that targets an opponent or a creature an opponent controls" | `{ "cast": { "spell": "instant_or_sorcery", "targets": { "one_of": ["opponent", { "creature": { "controller": "opponent" } }] } } }` |
|
|
1121
|
+
| "Whenever you cast a spell from anywhere other than your hand" / "from exile" (CR 601.2a) | `{ "cast": { "not_from": "hand" } }` / `{ "cast": { "from": "exile" } }` |
|
|
1122
|
+
| "Spells you cast from anywhere other than your hand cost {1} less" | `{ "cost_modifier": { "not_from": "hand", "reduce": 1 } }` (`from` works too) |
|
|
1123
|
+
| "Copy target activated or triggered ability you control [from a creature source]. You may choose new targets for the copy." (CR 707.10) | target `{ "stack_ability": { "controller": "you", "source": "creature" } }`, effect `{ "copy_spell": { "of": { "target": 0 }, "new_targets": true } }` (the copy has the same source, CR 707.10b) |
|
|
1124
|
+
| "discard a card at random" (CR 701.9b) | `{ "discard": { "amount": 1, "random": true } }` |
|
|
1125
|
+
| "exile a card from your graveyard at random" | `{ "choose_cards": { "from": { "graveyard": "you" }, "for": { "permanent": true }, "put": "exile", "random": true } }` |
|
|
1126
|
+
| "Flip a coin. If you win the flip, …" (CR 705.2) | `{ "flip_coins": {} }`, then the effect with `"if": { "amount": { "this_way": 0 }, "at_least": 1 }` (you call the flip: a decision) |
|
|
1127
|
+
| "Flip five coins. … X, where X is the number of coins that came up heads" | `{ "flip_coins": { "amount": 5, "count": "heads" } }`, X is `{ "this_way": 0 }` |
|
|
1128
|
+
| "Proliferate" / "Proliferate X times" (CR 701.34a) | `{ "proliferate": {} }` / `{ "proliferate": { "amount": "x" } }` (permanents and players with counters: one more of each kind each already has) |
|
|
1129
|
+
|
|
1130
|
+
The game's random number generator makes every random choice, so a game replays identically from
|
|
1131
|
+
its seed and decisions, and an exported game continues with the same results.
|
|
1132
|
+
|
|
1133
|
+
```json
|
|
1134
|
+
{ "name": "Example Spell Duelist", "mana_cost": "{1}{W}", "types": ["creature"], "subtypes": ["human", "samurai"],
|
|
1135
|
+
"power": 2, "toughness": 2,
|
|
1136
|
+
"abilities": [{ "triggered": { "when": { "cast": { "targets": { "creature": { "controller": "you" } } } },
|
|
1137
|
+
"effects": [{ "add_counters": { "counter": "+1/+1", "amount": 1, "to": "self" } },
|
|
1138
|
+
{ "scry": { "amount": 1 } }] } }] }
|
|
1139
|
+
```
|
|
1140
|
+
|
|
1141
|
+
### 4.24 Choosing between costs, optional additional costs and paying costs while resolving
|
|
1142
|
+
|
|
1143
|
+
| Oracle text | Data |
|
|
1144
|
+
| --- | --- |
|
|
1145
|
+
| "As an additional cost to cast this spell, behold a Goblin or pay {2}" (CR 601.2b) | `{ "additional_cost": { "cost": [{ "one_of": [{ "behold": { "card": { "subtypes": ["goblin"] } } }, { "mana": "{2}" }] }] } }` |
|
|
1146
|
+
| "… sacrifice a creature or pay {3}{B}", "… blight 1 or pay {3}", "… pay 5 life or pay {2}", "… exile two cards from your graveyard or pay {1}{W}" | the same, with any two or more costs in the `one_of` |
|
|
1147
|
+
| "{3}, {T}, Sacrifice an artifact or discard a nonland card: …" (CR 602.2b) | `"cost": [{ "mana": "{3}" }, "tap", { "one_of": [{ "sacrifice_permanent": { "filter": "artifact" } }, { "discard": { "card": { "not_types": ["land"] } } }] }]` |
|
|
1148
|
+
| "As an additional cost to cast this spell, you may behold a Dragon. … If a Dragon was beheld, …" (CR 118.8b) | `{ "additional_cost": { "cost": [{ "behold": { "card": { "subtypes": ["dragon"] } } }], "optional": true } }` and the condition `{ "cast_with": "additional_cost" }` |
|
|
1149
|
+
| "You may sacrifice an artifact or discard a card. If you do, draw a card." (CR 118.12) | `{ "pay": { "cost": [{ "one_of": [{ "sacrifice_permanent": { "filter": "artifact" } }, "discard"] }], "then": [{ "draw": { "amount": 1 } }] } }` |
|
|
1150
|
+
| "… When you do, it deals 2 damage to any target" | `then`: `[{ "reflexive_trigger": { "targets": ["any_target"], "effects": [ … ] } }]` |
|
|
1151
|
+
| "Ward—Pay 2 life", "Ward—Discard a card", "Ward—Discard a card or pay {2}" (CR 702.21a) | `{ "ward": { "cost": [{ "pay_life": 2 }] } }`, `[ "discard" ]`, `[{ "one_of": ["discard", { "mana": "{2}" }] }]` |
|
|
1152
|
+
| "Ward—Get five poison counters" | `{ "ward": { "cost": [{ "get_counters": { "counter": "poison", "amount": 5 } }] } }` |
|
|
1153
|
+
| "Exile X artifact cards from your graveyard: … -X/-X" (CR 601.2b) | `{ "exile_from_graveyard": { "count": "X", "card": { "types": ["artifact"] } } }` in the `cost` (X is announced, at most the number of matching cards; also `discard`, `exile_from_hand`) and the amount `"x"` in effects |
|
|
1154
|
+
| "As this land enters, you may behold a Jace. If you don't, this land enters tapped." (CR 614.12) | `{ "enters_tapped_unless_behold": { "card": { "subtypes": ["jace"] } } }` (the `enter_choice` option `{type: "behold", object: id}`) |
|
|
1155
|
+
| "Gift a Treasure … Return target spell to its owner's hand. If the gift was promised, …" (CR 702.174) | `"gift"` in `abilities`; the gift as the spell's first effect, `{ "create_token": { "token": "Treasure", "to": "gift_recipient", "if": { "cast_with": "gift" } } }`, then the rest (on a permanent: an `enters` trigger with `"if": { "cast_with": "gift" }`) |
|
|
1156
|
+
| "You may discard your hand. If you do, draw …" / "… Draw X cards, where X is the number of cards discarded this way" | `{ "may": { "effects": [{ "discard": { "hand": true } }], "then": [ … ] } }`; X is `{ "this_way": n }` of that effect (n: its position in the list) |
|
|
1157
|
+
| "Noncreature spells you cast have improvise." (CR 702.126a) | `{ "spells_have": { "spells": { "not_types": ["creature"] }, "keywords": ["improvise"] } }` (convoke too) |
|
|
1158
|
+
| "Counter target spell unless its controller pays {2}. If …, {4} instead" | two `counter_unless_pays` with opposite `if` conditions (`cost` takes a list of costs too) |
|
|
1159
|
+
|
|
1160
|
+
Which `one_of` option is paid is announced while casting or activating, after modes and before X
|
|
1161
|
+
(the `choose_cost_option` decision, CR 601.2b); only options that could be paid are offered, and a
|
|
1162
|
+
spell or ability is castable only if some option can be. An optional additional cost is announced
|
|
1163
|
+
with the `choose_kicker` decision (a spell has at most one kicker-like optional cost). While
|
|
1164
|
+
resolving, the payer picks one `pay` choice per way the costs could be paid, makes the costs'
|
|
1165
|
+
choices (which card to discard), then pays any mana; cancelling the mana counts as not paying,
|
|
1166
|
+
but cards already revealed for a behold or permanents tapped for a cost stay so.
|
|
1167
|
+
|
|
1168
|
+
```json
|
|
1169
|
+
{ "name": "Example Toll Titan", "mana_cost": "{2}{B}", "types": ["creature"], "subtypes": ["giant"],
|
|
1170
|
+
"power": 5, "toughness": 5,
|
|
1171
|
+
"abilities": [{ "additional_cost": { "cost": [{ "one_of": ["discard", { "mana": "{2}" }] }] } },
|
|
1172
|
+
{ "ward": { "cost": [{ "one_of": ["discard", { "mana": "{2}" }] }] } }] }
|
|
1173
|
+
```
|
|
1174
|
+
|
|
1175
|
+
### 4.25 Casting and mana: divided damage, casting restrictions, where mana came from
|
|
1176
|
+
|
|
1177
|
+
| Oracle text | Data |
|
|
1178
|
+
| --- | --- |
|
|
1179
|
+
| "deals 3 damage divided as you choose among one, two, or three targets" (CR 601.2d) | targets `[{ "filter": "any_target", "count": 3, "optional": true }]`, `"divide": 3`, effect `{ "distribute_damage": {} }` (at least one target; each gets at least 1) |
|
|
1180
|
+
| "deals X damage divided as you choose among any number of target creatures, where X is the number of lands you control" | `"count": "any"` with `"divide": { "count": { "each": "land", "controller": "you" } }` (an amount, computed as it's cast or put on the stack) |
|
|
1181
|
+
| "When you do, it deals 7 damage divided as you choose among one or two targets" | `reflexive_trigger` takes `divide` too |
|
|
1182
|
+
| "You can't cast this spell unless there are seven or more cards in your graveyard" (CR 601.3) | ability `{ "cast_restriction": { "only_if": { "amount": { "graveyard_size": "you" }, "at_least": 7 } } }` |
|
|
1183
|
+
| "You can't cast this spell during your first, second, or third turns of the game" | `"only_if": { "any": [{ "not": { "your_turn": true } }, { "amount": { "turns_taken": "you" }, "at_least": 4 }] }` (`turns_taken`: turns the player has begun, this one included) |
|
|
1184
|
+
| "if the amount of mana spent to cast that spell was less than its mana value" | `{ "amount": { "mana_spent": { "of": { "target": 0 } } }, "at_most": { "plus": [{ "mana_value": { "target": 0 } }, -1] } }` (bounds may be amounts; a spell countered by this one is read as it last existed, CR 608.2h) |
|
|
1185
|
+
| "if mana from a Treasure was spent to cast it" | `{ "amount": { "mana_spent": { "of": { "triggering": "object" }, "from": { "subtypes": ["treasure"] } } }, "at_least": 1 }` |
|
|
1186
|
+
| "costs {X} less to cast, where X is the greatest toughness among creatures you control" | `{ "cost_reduction": { "amount": { "greatest_toughness": { "each": "creature", "controller": "you" } } } }` |
|
|
1187
|
+
| "Spend this mana only to cast a spell from anywhere other than your hand" / "only to cast spells from exile" | `"spend_only": { "not_from": "hand" }` / `{ "from": "exile" }` (also with `spells`) |
|
|
1188
|
+
| "{T}: Add {G}{G}. Activate only if there is an Elf card in your graveyard." (CR 602.5b) | `{ "mana": { "cost": ["tap"], "produce": "{G}{G}", "only_if": condition } }` |
|
|
1189
|
+
| "If that spell would be put into a graveyard, exile it instead" (CR 614.1a) | `"exile_instead": true` on `cast_free`, `cast` or `play_permission`; a card spec instead of `true` limits it ("if an instant or sorcery spell cast this way …": `{ "any_of": [{ "types": ["instant"] }, { "types": ["sorcery"] }] }`) |
|
|
1190
|
+
| "you may cast it, and mana of any type can be spent to cast that spell" (while resolving) | `{ "cast": { "to": { "target": 0 }, "any_mana_type": true } }` |
|
|
1191
|
+
|
|
1192
|
+
Mana made by an artifact that isn't a land remembers its source's characteristics as it made the mana: pool
|
|
1193
|
+
mana in the player's `pool_sources`, restricted mana in its entry's `from`. When the pool holds the same type both
|
|
1194
|
+
from such a source and from another, paying offers `{type: "pay_mana", mana: "R", from: i}` (the remembered
|
|
1195
|
+
one) beside plain `{type: "pay_mana", mana: "R"}` (the other one first). The amount `mana_value` (of a
|
|
1196
|
+
selector) counts X on the stack (CR 202.3e).
|
|
1197
|
+
|
|
1198
|
+
```json
|
|
1199
|
+
{ "name": "Example Hoard Drake", "mana_cost": "{3}{B}{R}", "types": ["creature"], "subtypes": ["dragon"],
|
|
1200
|
+
"power": 4, "toughness": 4,
|
|
1201
|
+
"abilities": [
|
|
1202
|
+
{ "triggered": { "when": { "enters": {} }, "targets": [{ "filter": "any_target", "count": 2, "optional": true }],
|
|
1203
|
+
"divide": 4, "effects": [{ "distribute_damage": {} }] } },
|
|
1204
|
+
{ "triggered": { "when": { "cast": {} },
|
|
1205
|
+
"if": { "amount": { "mana_spent": { "of": { "triggering": "object" }, "from": { "subtypes": ["treasure"] } } },
|
|
1206
|
+
"at_least": 1 },
|
|
1207
|
+
"effects": [{ "draw": { "amount": 1 } }] } }] }
|
|
1208
|
+
```
|
|
1209
|
+
|
|
1210
|
+
### 4.26 More combat and damage: block and attack restrictions, prevention follow-ups, ending the turn
|
|
1211
|
+
|
|
1212
|
+
A `then` list on a prevention or dies-to-exile replacement happens right after it applies (CR 615.5,
|
|
1213
|
+
614.1a). Its effects need no choice; they read `{ "triggering": "amount" }` (the damage prevented),
|
|
1214
|
+
`"object"` (the damage source, or the card exiled instead) and `"player"` (that object's controller). A
|
|
1215
|
+
"When you do, …" is a `reflexive_trigger` in the list (CR 603.12).
|
|
1216
|
+
|
|
1217
|
+
| Oracle text | Data |
|
|
1218
|
+
| --- | --- |
|
|
1219
|
+
| "This creature can block only creatures with flying" / "can't block non-Spirit creatures" (CR 509.1b) | `{ "can_block_only": { "filter": { "creature": { "keywords": ["flying"] } } } }` / `"filter": { "creature": { "subtypes": ["spirit"] } }` |
|
|
1220
|
+
| "Whenever this creature attacks for the first time each turn" | trigger `{ "attacks": { "first_time_this_turn": true } }` |
|
|
1221
|
+
| "Whenever you attack with creatures with total power 12 or greater for the first time each turn" | trigger `{ "player_attacks": { "attacking_power": { "at_least": 12 }, "first_time_this_turn": true } }` |
|
|
1222
|
+
| "After this phase, there is an additional combat phase. Only land creatures can attack during that combat phase." (CR 508.1c) | `{ "additional_phases": { "phases": ["combat"], "attackers": { "creature": { "types": ["land"] } } } }` |
|
|
1223
|
+
| "If a creature you control that entered this turn would deal damage, it deals twice that much damage instead" | `{ "damage_replacement": { "by": { "each": { "creature": { "entered_this_turn": true } }, "controller": "you" }, "multiply": 2 } }` |
|
|
1224
|
+
| "Until your next turn, if a source would deal damage to that player or a permanent that player controls, it deals double that damage instead" | effect `{ "damage_replacement": { "to": { "triggering": "player" }, "permanents": true, "multiply": 2, "duration": "until_your_next_turn" } }` |
|
|
1225
|
+
| "If damage would be dealt to this creature, prevent that damage and put that many +1/+1 counters on him" | `{ "prevent_damage": { "to": "self", "then": [{ "add_counters": { "counter": "+1/+1", "amount": { "triggering": "amount" }, "to": "self" } }] } }` |
|
|
1226
|
+
| "The next time a source of your choice would deal damage to you this turn, prevent that damage. When damage is prevented this way, … deals that much damage to that source's controller" (CR 615.8, 609.7a) | effect `{ "prevent_damage": { "to": "you", "by": "chosen_source", "next_time": true, "then": [{ "reflexive_trigger": { "effects": [{ "deal_damage": { "amount": { "triggering": "amount" }, "to": { "triggering": "player" } } }] } }] } }` |
|
|
1227
|
+
| "Prevent all damage that would be dealt by up to one target creature for as long as this Saga remains on the battlefield" (CR 611.2b) | effect `prevent_damage` with `"by": { "target": 0 }, "duration": "while_source_on_battlefield"` |
|
|
1228
|
+
| "Prevent all damage that would be dealt to creatures you control this turn" (the creatures as damage is dealt, CR 611.2c) | effect `{ "prevent_damage": { "to_each": { "each": "creature", "controller": "you" } } }` |
|
|
1229
|
+
| "Damage isn't removed from this creature during cleanup steps" (CR 514.2) | `"keywords": ["keeps_damage"]` |
|
|
1230
|
+
| "If a creature an opponent controls would die, exile it instead. When you do, create a 2/2 Wolf" | `{ "exile_if_dies": { "affects": { … }, "then": [{ "reflexive_trigger": { "effects": [{ "create_token": … }] } }] } }` (the card is also "exiled with" this permanent: `"exiled_with"`) |
|
|
1231
|
+
| "It fights target creature an opponent controls. When excess damage is dealt to the creature an opponent controls this way, …" (CR 120.4a) | `fight`, then `{ "reflexive_trigger": { "if": { "amount": { "this_way": 0 }, "at_least": 1 }, … } }` (a fight's `this_way` is the excess damage dealt to its `with` creature) |
|
|
1232
|
+
| "End the turn." (CR 725.1) | `"end_the_turn"` (the spell's later effects don't happen) |
|
|
1233
|
+
|
|
1234
|
+
A source that left the zone it was in still deals damage with the deathtouch, lifelink and wither it had
|
|
1235
|
+
(CR 702.2e, 702.15c, 702.80b).
|
|
1236
|
+
|
|
1237
|
+
```json
|
|
1238
|
+
{ "name": "Example Sanctum Healer", "mana_cost": "{3}{W}{W}", "types": ["creature"], "subtypes": ["human", "cleric"],
|
|
1239
|
+
"power": 4, "toughness": 4,
|
|
1240
|
+
"abilities": [{ "prevent_damage": { "to": "self", "then": [{ "add_counters": { "counter": "+1/+1",
|
|
1241
|
+
"amount": { "triggering": "amount" }, "to": "self" } }] } },
|
|
1242
|
+
{ "triggered": { "when": { "attacks": { "first_time_this_turn": true } },
|
|
1243
|
+
"effects": [{ "additional_phases": { "phases": ["combat"] } }] } }] }
|
|
1244
|
+
```
|
|
1245
|
+
|
|
1246
|
+
### 4.27 Names and card types set by statics, protection from opponents, crewing power, "for as long as you control"
|
|
1247
|
+
|
|
1248
|
+
| Oracle text | Data |
|
|
1249
|
+
| --- | --- |
|
|
1250
|
+
| "Enchanted creature loses all abilities and is a black Skeleton creature with base power and toughness 1/1" (it loses its other card types, CR 205.1a) | `static` with `"affects": "attached"`, `"loses_abilities": true`, `"colors": ["B"]`, `"set_types": ["creature"]`, `"creature_types": ["skeleton"]`, `"base_power_toughness": { "power": 1, "toughness": 1 }` |
|
|
1251
|
+
| "… named Legitimate Businessperson" (CR 612.8, layer 3) | `"name": "Legitimate Businessperson"` on the same `static` (the legend rule and name filters see it) |
|
|
1252
|
+
| "Enchanted permanent is a Construct creature with base power and toughness 5/5 in addition to its other types" | `static` with `"types": ["creature"]`, `"subtypes": ["construct"]`, `"base_power_toughness"` |
|
|
1253
|
+
| "You have protection from each of your opponents" (CR 702.16k) | `{ "player_rule": { "rule": "protection_from_opponents" } }` (opponents' spells and abilities can't target you; damage from their sources is prevented) |
|
|
1254
|
+
| "Protection from each of your opponents" on a permanent | `{ "protection_from": "each_opponent" }` (also grantable by `static` and `become`) |
|
|
1255
|
+
| "Each other creature you control has hexproof from each of its colors" (CR 702.11g) | `static` granting `{ "hexproof_from": "its_colors" }` |
|
|
1256
|
+
| "This creature saddles Mounts and crews Vehicles as though its power were 2 greater" | `{ "tapping_power": { "for": ["crew", "saddle"], "plus": 2 } }` |
|
|
1257
|
+
| "… crews Vehicles using its toughness rather than its power" / "stations permanents using its toughness" when greater (CR 702.184c) | `"use": "toughness"` / `"use": "toughness_if_greater"` with `"for": ["station"]` (a `static` can grant it) |
|
|
1258
|
+
| "Activated abilities of artifacts and creatures can't be activated" (mana abilities included) / "its activated abilities can't be activated" | `static` handing out the keyword `abilities_cant_be_activated` (`"affects": "attached"` for an Aura); `player_rule` `cant_activate` still exempts mana abilities |
|
|
1259
|
+
| "Spells and abilities can't be countered" | two `player_rule`s, `spells_cant_be_countered` and `abilities_cant_be_countered`, with `"affects": "each"` |
|
|
1260
|
+
| "Gain control of target creature for as long as you control this Vehicle" (CR 611.2b) | `gain_control` or `become` with `"duration": "while_you_control_source"` (ends when this leaves or changes control, and never starts if that has already happened) |
|
|
1261
|
+
| "Equipped creature has '{1}, {T}: Tap target creature. Return Trusty Boomerang to its owner's hand.'" (CR 607.1a) | selector `"granter"`: the permanent whose `static` granted the ability (`{ "return_to_hand": { "to": "granter" } }`) |
|
|
1262
|
+
| "As this land enters, choose a basic land type. Then you may pay 2 life. If you don't, it enters tapped." | `choose_basic_land_type` then `enters_tapped_unless_pay_life`: several "as this enters" choices are asked in printed order (CR 614.12) |
|
|
1263
|
+
|
|
1264
|
+
An Aura or Equipment gets a new timestamp each time it becomes attached, and a permanent each time it
|
|
1265
|
+
transforms (CR 613.7e, 613.7g), so a static of an Equipment attached after "loses all abilities" still
|
|
1266
|
+
grants its abilities.
|
|
1267
|
+
|
|
1268
|
+
```json
|
|
1269
|
+
{ "name": "Example Witness Shroud", "mana_cost": "{U}", "types": ["enchantment"], "subtypes": ["aura"],
|
|
1270
|
+
"abilities": [{ "enchant": "creature" },
|
|
1271
|
+
{ "static": { "affects": "attached", "loses_abilities": true, "set_types": ["creature"],
|
|
1272
|
+
"creature_types": ["citizen"], "base_power_toughness": { "power": 1, "toughness": 1 },
|
|
1273
|
+
"name": "Nobody in Particular" } }] }
|
|
1274
|
+
```
|
|
1275
|
+
|
|
1276
|
+
### 4.28 Triggers, turn history and the stack: last turn, land plays, copying "that ability"
|
|
1277
|
+
|
|
1278
|
+
| Oracle text | Data |
|
|
1279
|
+
| --- | --- |
|
|
1280
|
+
| "if an opponent was dealt noncombat damage last turn" (whoever's turn it was) | `{ "amount": { "noncombat_damage_last_turn": "each_opponent" }, "at_least": 1 }` |
|
|
1281
|
+
| "Whenever you play a land" / "… from anywhere other than your hand" / "from exile" (CR 305.1) | trigger `{ "plays_land": {} }` / `{ "plays_land": { "not_from": "hand" } }` / `{ "from": "exile" }` (`controller` as for `cast`) |
|
|
1282
|
+
| "if you've played a land or cast a spell this turn from anywhere other than your hand" | `{ "amount": { "plus": [{ "lands_played_this_turn": { "by": "you", "not_from": "hand" } }, { "spells_cast_this_turn": { "by": "you", "not_from": "hand" } }] }, "at_least": 1 }` |
|
|
1283
|
+
| "Whenever a permanent you control enters from exile" | `{ "enters": { "subject": { "each": "permanent", "controller": "you" }, "from": "exile" } }` |
|
|
1284
|
+
| "you may exchange life totals with target opponent. If you lost life this way, draw that many cards" (CR 608.2c) | `[{ "may": { "effects": [{ "exchange_life_totals": { "with": { "target": 0 } } }] } }, { "draw": { "amount": { "this_way": 0 } } }]` (a `may` records what its effects did) |
|
|
1285
|
+
| "two target nonland permanents that share a card type" (CR 701.12b) | second target `{ "filter": …, "distinct_from": [0], "shares_type_with": 0 }`; on resolution `"if": { "share_card_type": [{ "target": 0 }, { "target": 1 }] }` |
|
|
1286
|
+
| "Whenever you cast a spell that targets one or more creatures, those creatures …" | trigger `{ "cast": { "targets": "creature" } }`, selector `{ "triggering": "targets" }` (the targets that matched) |
|
|
1287
|
+
| "an instant or sorcery spell with a single target" | `{ "cast": { "spell": "instant_or_sorcery", "single_target": true } }` |
|
|
1288
|
+
| "You may choose new targets for that spell" (CR 115.7d) | `{ "choose_new_targets": { "of": { "triggering": "object" } } }` (keep or replace each target) |
|
|
1289
|
+
| "When you next activate an exhaust ability this turn, copy it" (CR 707.10) | `{ "delayed_trigger": { "when": { "activates_ability": { "exhaust": true } }, "this_turn": "once", "effects": [{ "copy_spell": { "of": { "triggering": "ability" }, "new_targets": true } }] } }` |
|
|
1290
|
+
| "When you next cast a Lesson spell this turn, copy it" | the same with `{ "cast": { "spell": { "spell": { "subtypes": ["lesson"] } } } }` and `"of": { "triggering": "object" }` |
|
|
1291
|
+
| "Whenever a creature you control attacking causes a triggered ability of that creature to trigger, … copy that ability" | trigger `{ "ability_triggers": { "subject": { "each": "creature", "controller": "you" }, "cause": "attacks" } }`, `copy_spell` of `{ "triggering": "ability" }` (nothing to copy once it left the stack) |
|
|
1292
|
+
| "Whenever a creature you control boasts" (CR 702.142b) | `{ "activates_ability": { "boast": true } }` |
|
|
1293
|
+
| "Whenever this creature becomes tapped to pay a teamwork cost" | `{ "becomes_tapped": { "for": "teamwork" } }` (it triggers once the spell is cast) |
|
|
1294
|
+
| "you may copy it. If you do, those spells gain wither" | `copy_spell` with `"except": { "keywords": ["wither"] }`, then `{ "grant": { "keywords": ["wither"], "to": { "triggering": "object" } } }` (a spell on the stack can gain keywords) |
|
|
1295
|
+
| "Instant and sorcery spells you cast from your hand have cascade" | ability `{ "spell_keywords": { "spells": { "spell": { "any_of": ["instant", "sorcery"] } }, "from": "hand", "keywords": ["cascade"] } }` |
|
|
1296
|
+
| "search your library for up to X basic land cards with different names" | `{ "search_library": { "for": { "supertypes": ["basic"], "types": ["land"] }, "count": "x", "different_names": true } }` |
|
|
1297
|
+
| "The first time you flip one or more coins each turn, those coins come up heads and you win those flips" (CR 705.3) | ability `"first_flips_win"` |
|
|
1298
|
+
| "Roll a d20" / "roll two six-sided dice" (CR 706) | `{ "roll_dice": { "sides": 20 } }` / `{ "roll_dice": { "amount": 2 } }`; the total is `{ "this_way": n }` (a results table is effects with an `if` on it) |
|
|
1299
|
+
|
|
1300
|
+
```json
|
|
1301
|
+
{ "name": "Example Echo Drillmaster", "mana_cost": "{2}{R}", "types": ["creature"], "subtypes": ["goblin"],
|
|
1302
|
+
"power": 2, "toughness": 2,
|
|
1303
|
+
"abilities": [
|
|
1304
|
+
{ "triggered": { "when": { "attacks": {} }, "effects": [{ "deal_damage": { "amount": 1, "to": "each_opponent" } }] } },
|
|
1305
|
+
{ "triggered": { "when": { "ability_triggers": { "subject": { "each": "creature", "controller": "you" }, "cause": "attacks" } },
|
|
1306
|
+
"effects": [{ "may": { "effects": [{ "copy_spell": { "of": { "triggering": "ability" } } }] } }] } }] }
|
|
1307
|
+
```
|
|
1308
|
+
|
|
1309
|
+
### 4.29 Paradigm, foretell, suspend, hideaway, devour, meld and computed endure
|
|
1310
|
+
|
|
1311
|
+
| Oracle text | Data |
|
|
1312
|
+
| --- | --- |
|
|
1313
|
+
| "Paradigm" (CR 702.192a) | `"keywords": ["paradigm"]` on an instant or sorcery: as it finishes resolving it's exiled; the first time a spell you control with its name resolves this game, at the beginning of each of your precombat main phases you create a copy of the card in exile and may cast it without paying its mana cost (an `effect_choice` of `{type: "cast"}` or `"done"`) |
|
|
1314
|
+
| "Foretell {2}{R}" (CR 702.143a) | ability `{ "foretell": { "cost": "{2}{R}" } }`: during your turn the priority option `{type: "special_action", object: id, ability: ref}` pays {2} and exiles it face down; on a later turn it's cast from exile with `via: "alt:<ref>"`; condition `{ "cast_with": "foretell" }` |
|
|
1315
|
+
| "Suspend 3—{2}{W}" (CR 702.62a) | ability `{ "suspend": { "amount": 3, "cost": "{2}{W}" } }`: the special action exiles it with three time counters whenever you could begin to cast it |
|
|
1316
|
+
| "exile it with four time counters on it. If it doesn't have suspend, it gains suspend" | effect `{ "suspend": { "to": …, "amount": 4 } }` (a spell, a card or a permanent); a card that has suspend from the start is `"keywords": ["suspend"]` |
|
|
1317
|
+
| suspended cards (CR 702.62b) | automatic: at the beginning of its owner's upkeep a time counter is removed; when the last is removed they may play it without paying its mana cost (`effect_choice` `{type: "cast"}` / `{type: "play_land"}`), and a creature spell cast this way has haste while they control it |
|
|
1318
|
+
| "Hideaway 4" (CR 702.75a) | ability `{ "hideaway": { "amount": 4 } }`: the exiled card is `"exiled_with"` the permanent, face down, and only its controller may look at it |
|
|
1319
|
+
| "You may play the exiled card without paying its mana cost" | `{ "cast_free": { "to": "exiled_with", "play": true } }` (`play: true` also plays a land, on your turn with a land play left, CR 305.2a, 305.3) |
|
|
1320
|
+
| "look at the top N cards, exile one face down" | `{ "dig": { "amount": N, "put": "exile", "face_down": true, "link": true } }` (`link`: exiled with the source) |
|
|
1321
|
+
| "Devour land 3" (CR 702.82c) / "Devour 2" | ability `{ "devour": { "amount": 3, "each": "land" } }` / `{ "devour": { "amount": 2 } }`: as it enters, `enter_choice` `{type: "devour", object: id}` picks permanents one at a time, then `{type: "done"}`; it enters with N +1/+1 counters for each and remembers how many it devoured |
|
|
1322
|
+
| Meld cards (CR 713) | `"layout": "meld"`, `"faces": [front, combined back face]`; both cards of the pair carry the same back face (e.g. Ragnarok, Divine Deliverance on Fang and on Vanille). The back face is up only on the melded permanent; when it leaves the battlefield both cards go to the new zone (CR 713.8) |
|
|
1323
|
+
| "if you both own and control this and a creature named Fang, …, exile them, then meld them" (CR 701.42a) | condition `{ "meld_with": "Fang, Fearless l'Cie" }`; effect `{ "meld": { "with": "Fang, Fearless l'Cie" } }` (cards that don't form a meld pair stay exiled, CR 701.42c) |
|
|
1324
|
+
| "endures X" / "endures X, where X is the number of counters on this creature" (CR 701.63a) | `{ "endure": { "amount": "x" } }` / `{ "endure": { "amount": { "counters": { "of": "self" } } } }`: the Spirit token is X/X |
|
|
1325
|
+
| "Proliferate X times, where X is the number of planeswalker types among planeswalkers you control" | `{ "proliferate": { "amount": { "distinct_planeswalker_types": { "each": "planeswalker", "controller": "you" } } } }` |
|
|
1326
|
+
|
|
1327
|
+
```json
|
|
1328
|
+
{ "name": "Example Lesson of Embers", "mana_cost": "{2}{R}", "types": ["sorcery"], "subtypes": ["lesson"],
|
|
1329
|
+
"keywords": ["paradigm"],
|
|
1330
|
+
"abilities": [{ "spell": { "targets": ["any_target"],
|
|
1331
|
+
"effects": [{ "deal_damage": { "amount": 2, "to": { "target": 0 } } }] } },
|
|
1332
|
+
{ "foretell": { "cost": "{1}{R}" } }] }
|
|
1333
|
+
```
|
|
1334
|
+
|
|
1335
|
+
---
|
|
1336
|
+
|
|
1337
|
+
## 5. Worked examples
|
|
1338
|
+
|
|
1339
|
+
**Vanilla creature.** "Grizzly Bears {1}{G}, Creature — Bear 2/2":
|
|
1340
|
+
```json
|
|
1341
|
+
{ "name": "Grizzly Bears", "mana_cost": "{1}{G}", "types": ["creature"], "subtypes": ["bear"],
|
|
1342
|
+
"power": 2, "toughness": 2 }
|
|
1343
|
+
```
|
|
1344
|
+
|
|
1345
|
+
**Keyword creature.** "Raging Goblin {R}, Creature — Goblin Berserker 1/1, Haste":
|
|
1346
|
+
```json
|
|
1347
|
+
{ "name": "Raging Goblin", "mana_cost": "{R}", "types": ["creature"], "subtypes": ["goblin", "berserker"],
|
|
1348
|
+
"keywords": ["haste"], "power": 1, "toughness": 1, "rules_text": "Haste" }
|
|
1349
|
+
```
|
|
1350
|
+
|
|
1351
|
+
**Instant with two effects.** "Revitalize {1}{W}: You gain 3 life. Draw a card.":
|
|
1352
|
+
```json
|
|
1353
|
+
{ "name": "Revitalize", "mana_cost": "{1}{W}", "types": ["instant"],
|
|
1354
|
+
"abilities": [{ "spell": { "effects": [{ "gain_life": { "amount": 3 } }, { "draw": { "amount": 1 } }] } }],
|
|
1355
|
+
"rules_text": "You gain 3 life. Draw a card." }
|
|
1356
|
+
```
|
|
1357
|
+
|
|
1358
|
+
**Planeswalker.** "+1: Creatures you control get +1/+1 until end of turn. −2: Put a +1/+1 counter on each creature you control.":
|
|
1359
|
+
```json
|
|
1360
|
+
{ "name": "Vessa, Rally Captain", "mana_cost": "{2}{W}{W}", "types": ["planeswalker"], "subtypes": ["vessa"],
|
|
1361
|
+
"loyalty": 3,
|
|
1362
|
+
"abilities": [
|
|
1363
|
+
{ "loyalty": { "cost": 1, "effects": [
|
|
1364
|
+
{ "pump": { "power": 1, "toughness": 1, "to": { "each": "creature", "controller": "you" } } }] } },
|
|
1365
|
+
{ "loyalty": { "cost": -2, "effects": [
|
|
1366
|
+
{ "add_counters": { "counter": "+1/+1", "amount": 1, "to": { "each": "creature", "controller": "you" } } }] } }
|
|
1367
|
+
] }
|
|
1368
|
+
```
|
|
1369
|
+
|
|
1370
|
+
**Rejected with a path.** A typo or an unknown keyword:
|
|
1371
|
+
```json
|
|
1372
|
+
{ "name": "Gale Drifter", "mana_cost": "{2}{U}", "types": ["creature"], "keywords": ["flyng"], "power": 2, "toughness": 2 }
|
|
1373
|
+
```
|
|
1374
|
+
→ `keywords[0]: unknown keyword "flyng" (supported: …)`.
|
|
1375
|
+
|
|
1376
|
+
---
|
|
1377
|
+
|
|
1378
|
+
## 6. Decks
|
|
1379
|
+
|
|
1380
|
+
A deck document references cards by name (or id) and carries the definitions it uses:
|
|
1381
|
+
|
|
1382
|
+
```json
|
|
1383
|
+
{ "cards": [ { "name": "Forest", ... }, { "name": "Grizzly Bears", ... } ],
|
|
1384
|
+
"main": [ { "card": "Forest", "count": 24 }, { "card": "Grizzly Bears", "count": 4 } ] }
|
|
1385
|
+
```
|
|
1386
|
+
|
|
1387
|
+
A deck is valid with at least 60 cards and at most 4 copies of each non-basic card
|
|
1388
|
+
(CR 100.2a). `Enjine::Player.new(name:, deck:)` takes this document directly;
|
|
1389
|
+
`player.ready?` reports validity and `player.deck.errors` explains it.
|
|
1390
|
+
|
|
1391
|
+
**Cards outside the game** (CR 400.11a). The engine plays single games, so a deck may
|
|
1392
|
+
list the cards its player owns outside the game, a constructed sideboard, as
|
|
1393
|
+
`outside`. These cards don't count toward the 60. There can be at most 15 of them,
|
|
1394
|
+
and the four-copy limit counts the deck and the outside list together (CR 100.4a).
|
|
1395
|
+
Errors carry a path, for example `outside[2].card: "Black Lotus" is not defined in cards`.
|
|
1396
|
+
|
|
1397
|
+
```json
|
|
1398
|
+
{ "cards": [ ... ],
|
|
1399
|
+
"main": [ { "card": "Forest", "count": 24 }, { "card": "Grizzly Bears", "count": 4 } ],
|
|
1400
|
+
"outside": [ { "card": "Hill Giant", "count": 2 } ] }
|
|
1401
|
+
```
|
|
1402
|
+
|
|
1403
|
+
During a game these cards aren't objects (CR 400.11). Each player's list is
|
|
1404
|
+
`state.players[i].outside`, a list of card ids that only that player sees in their
|
|
1405
|
+
view. A `play_from` ability with `"outside"` among its zones offers them as
|
|
1406
|
+
`{ "type": "cast", "object": "outside:hill_giant", "via": ... }` (or `play_land`). The
|
|
1407
|
+
object is created as the card enters the game, and that player owns it (CR 108.3).
|
|
1408
|
+
|
|
1409
|
+
---
|
|
1410
|
+
|
|
1411
|
+
## 7. Validating
|
|
1412
|
+
|
|
1413
|
+
From the shell (exit code 0 means all valid, 1 means some invalid):
|
|
1414
|
+
|
|
1415
|
+
```sh
|
|
1416
|
+
bundle exec exe/enjine --validate cards.json
|
|
1417
|
+
```
|
|
1418
|
+
|
|
1419
|
+
`cards.json` is a JSON array of card blocks (or a single block). The report:
|
|
1420
|
+
|
|
1421
|
+
```json
|
|
1422
|
+
{ "rules_version": "2026-11-06", "engine_version": "0.1.0",
|
|
1423
|
+
"valid": [ { "index": 0, "id": "lightning_bolt", "name": "Lightning Bolt", "card": { ...canonical... } } ],
|
|
1424
|
+
"invalid": [ { "index": 1, "name": "Gale Drifter", "errors": ["keywords[0]: unknown keyword \"flyng\" ..."] } ] }
|
|
1425
|
+
```
|
|
1426
|
+
|
|
1427
|
+
To play-test a whole folder of cards (every `.json` file below it, each a card or an
|
|
1428
|
+
array of cards), use `--verify`. It validates every card, builds random decks that
|
|
1429
|
+
together include each valid card, plays rules-monitored games with them, and reports
|
|
1430
|
+
invalid cards, cards never used in play (cast, played, activated, triggered or
|
|
1431
|
+
resolved), and any rules finding or engine error with its seed. Exit code 1 means
|
|
1432
|
+
something needs a look; `--json` prints the report as JSON.
|
|
1433
|
+
|
|
1434
|
+
```sh
|
|
1435
|
+
bundle exec exe/enjine --verify cards/ --games 20 --seed 1
|
|
1436
|
+
```
|
|
1437
|
+
|
|
1438
|
+
From Ruby (`require "enjine/verify"`, then `Enjine::Verify.new(dir, games:, seed:).run`
|
|
1439
|
+
returns the same report as a Hash).
|
|
1440
|
+
|
|
1441
|
+
```ruby
|
|
1442
|
+
require "enjine"
|
|
1443
|
+
|
|
1444
|
+
begin
|
|
1445
|
+
defn = Enjine::CardDefinition.from_h(data) # data: Hash parsed from JSON
|
|
1446
|
+
store(defn.id, defn.to_h) # canonical, round-trips exactly
|
|
1447
|
+
rescue Enjine::InvalidCardDefinition => e
|
|
1448
|
+
warn e.errors # every problem, each with a path
|
|
1449
|
+
end
|
|
1450
|
+
```
|
|
1451
|
+
|
|
1452
|
+
Error messages always start with a path into your data block
|
|
1453
|
+
(`abilities[0].spell.effects[1].deal_damage.amount: …`), so an agent can fix the
|
|
1454
|
+
exact field it got wrong.
|
|
1455
|
+
|
|
1456
|
+
## 8. A database schema that works
|
|
1457
|
+
|
|
1458
|
+
```sql
|
|
1459
|
+
CREATE TABLE enjine_cards (
|
|
1460
|
+
id TEXT PRIMARY KEY, -- CardDefinition#id, e.g. 'lightning_bolt'
|
|
1461
|
+
name TEXT NOT NULL UNIQUE,
|
|
1462
|
+
data JSONB NOT NULL, -- CardDefinition#to_h (canonical)
|
|
1463
|
+
printing_set TEXT, -- set code printed on the card, e.g. 'trk'
|
|
1464
|
+
printing_number TEXT, -- collector number printed on the card, e.g. '12'
|
|
1465
|
+
rules_version TEXT NOT NULL, -- report.rules_version
|
|
1466
|
+
engine_version TEXT NOT NULL, -- report.engine_version
|
|
1467
|
+
updated_at TIMESTAMPTZ DEFAULT now()
|
|
1468
|
+
);
|
|
1469
|
+
```
|
|
1470
|
+
|
|
1471
|
+
When building a game, load the deck's rows from `enjine_cards` and pass their
|
|
1472
|
+
`data` as the deck's `cards`.
|