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.
Files changed (269) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +28 -0
  3. data/LICENSE.txt +21 -0
  4. data/README.md +201 -0
  5. data/SUPPORTED.md +387 -0
  6. data/docs/ARCHITECTURE.md +141 -0
  7. data/docs/DESIGN.md +139 -0
  8. data/docs/MECHANICS_COVERAGE.md +670 -0
  9. data/docs/guides/adding-a-mechanic.md +60 -0
  10. data/docs/guides/api.md +614 -0
  11. data/docs/guides/card-data-format.md +1472 -0
  12. data/docs/guides/deck-builder.md +157 -0
  13. data/docs/plans/standard.md +371 -0
  14. data/docs/reference/abilities.md +1680 -0
  15. data/docs/reference/costs.md +339 -0
  16. data/docs/reference/counters.md +121 -0
  17. data/docs/reference/decisions.md +102 -0
  18. data/docs/reference/effects.md +2340 -0
  19. data/docs/reference/filters.md +172 -0
  20. data/docs/reference/keywords.md +647 -0
  21. data/docs/reference/subtypes.md +169 -0
  22. data/docs/reference/supertypes.md +31 -0
  23. data/docs/reference/triggers.md +797 -0
  24. data/docs/reference/types.md +216 -0
  25. data/exe/enjine +7 -0
  26. data/lib/enjine/actions/activate_mana_ability.rb +24 -0
  27. data/lib/enjine/actions/cast.rb +57 -0
  28. data/lib/enjine/actions/pass.rb +18 -0
  29. data/lib/enjine/actions/play_land.rb +52 -0
  30. data/lib/enjine/actions/special_action.rb +37 -0
  31. data/lib/enjine/actions.rb +20 -0
  32. data/lib/enjine/agents/busy.rb +27 -0
  33. data/lib/enjine/agents/random.rb +28 -0
  34. data/lib/enjine/card_definition.rb +433 -0
  35. data/lib/enjine/card_faces.rb +116 -0
  36. data/lib/enjine/catalog.rb +56 -0
  37. data/lib/enjine/characteristics.rb +39 -0
  38. data/lib/enjine/cli/text.rb +295 -0
  39. data/lib/enjine/cli.rb +312 -0
  40. data/lib/enjine/decisions/bottom.rb +29 -0
  41. data/lib/enjine/decisions/casting.rb +188 -0
  42. data/lib/enjine/decisions/combat.rb +75 -0
  43. data/lib/enjine/decisions/discard.rb +29 -0
  44. data/lib/enjine/decisions/effect_choice.rb +21 -0
  45. data/lib/enjine/decisions/enter_choice.rb +26 -0
  46. data/lib/enjine/decisions/legend_rule.rb +21 -0
  47. data/lib/enjine/decisions/mulligan.rb +26 -0
  48. data/lib/enjine/decisions/order_triggers.rb +21 -0
  49. data/lib/enjine/decisions/play_or_draw.rb +19 -0
  50. data/lib/enjine/decisions/priority.rb +22 -0
  51. data/lib/enjine/decisions/untap_choice.rb +35 -0
  52. data/lib/enjine/decisions.rb +22 -0
  53. data/lib/enjine/deck.rb +151 -0
  54. data/lib/enjine/deck_builder.rb +454 -0
  55. data/lib/enjine/docs.rb +90 -0
  56. data/lib/enjine/engine/ability_layers.rb +118 -0
  57. data/lib/enjine/engine/attachments.rb +86 -0
  58. data/lib/enjine/engine/casting/alt_casting.rb +254 -0
  59. data/lib/enjine/engine/casting/cast_choices.rb +292 -0
  60. data/lib/enjine/engine/casting/casting.rb +326 -0
  61. data/lib/enjine/engine/casting/cost_options.rb +99 -0
  62. data/lib/enjine/engine/casting/costs.rb +117 -0
  63. data/lib/enjine/engine/casting/payment.rb +353 -0
  64. data/lib/enjine/engine/casting/permissions.rb +412 -0
  65. data/lib/enjine/engine/casting/target_counts.rb +113 -0
  66. data/lib/enjine/engine/casting/waterbend.rb +90 -0
  67. data/lib/enjine/engine/characteristics.rb +379 -0
  68. data/lib/enjine/engine/class_levels.rb +25 -0
  69. data/lib/enjine/engine/combat.rb +315 -0
  70. data/lib/enjine/engine/copies.rb +208 -0
  71. data/lib/enjine/engine/damage.rb +196 -0
  72. data/lib/enjine/engine/designations.rb +46 -0
  73. data/lib/enjine/engine/emblems.rb +37 -0
  74. data/lib/enjine/engine/entering.rb +156 -0
  75. data/lib/enjine/engine/face_down.rb +30 -0
  76. data/lib/enjine/engine/faces.rb +222 -0
  77. data/lib/enjine/engine/follow_ups.rb +27 -0
  78. data/lib/enjine/engine/mana_sources.rb +230 -0
  79. data/lib/enjine/engine/phasing.rb +74 -0
  80. data/lib/enjine/engine/pregame.rb +95 -0
  81. data/lib/enjine/engine/priority.rb +47 -0
  82. data/lib/enjine/engine/resolution.rb +168 -0
  83. data/lib/enjine/engine/rules.rb +287 -0
  84. data/lib/enjine/engine/sagas.rb +41 -0
  85. data/lib/enjine/engine/speed.rb +104 -0
  86. data/lib/enjine/engine/spell_grants.rb +46 -0
  87. data/lib/enjine/engine/state_based_actions.rb +157 -0
  88. data/lib/enjine/engine/triggers.rb +364 -0
  89. data/lib/enjine/engine/turn_history.rb +240 -0
  90. data/lib/enjine/engine/turn_structure.rb +224 -0
  91. data/lib/enjine/engine/zones.rb +278 -0
  92. data/lib/enjine/engine.rb +55 -0
  93. data/lib/enjine/errors.rb +62 -0
  94. data/lib/enjine/events.rb +161 -0
  95. data/lib/enjine/frames.rb +84 -0
  96. data/lib/enjine/fuzz.rb +112 -0
  97. data/lib/enjine/game.rb +204 -0
  98. data/lib/enjine/history.rb +26 -0
  99. data/lib/enjine/mana.rb +142 -0
  100. data/lib/enjine/mechanics/abilities/additional_triggers.rb +86 -0
  101. data/lib/enjine/mechanics/abilities/chosen_values.rb +213 -0
  102. data/lib/enjine/mechanics/abilities/cost_changes.rb +154 -0
  103. data/lib/enjine/mechanics/abilities/damage_replacements.rb +569 -0
  104. data/lib/enjine/mechanics/abilities/double_counters.rb +71 -0
  105. data/lib/enjine/mechanics/abilities/entering_choices.rb +91 -0
  106. data/lib/enjine/mechanics/abilities/entering_copy.rb +69 -0
  107. data/lib/enjine/mechanics/abilities/enters_tapped.rb +159 -0
  108. data/lib/enjine/mechanics/abilities/enters_with_counters.rb +116 -0
  109. data/lib/enjine/mechanics/abilities/mana_ability.rb +209 -0
  110. data/lib/enjine/mechanics/abilities/play_permissions.rb +527 -0
  111. data/lib/enjine/mechanics/abilities/player_rules.rb +169 -0
  112. data/lib/enjine/mechanics/abilities/replacement_effects.rb +368 -0
  113. data/lib/enjine/mechanics/abilities/restrictions.rb +144 -0
  114. data/lib/enjine/mechanics/abilities/spell_keywords.rb +55 -0
  115. data/lib/enjine/mechanics/abilities/stack_abilities.rb +542 -0
  116. data/lib/enjine/mechanics/abilities/static.rb +365 -0
  117. data/lib/enjine/mechanics/abilities/tapping_power.rb +71 -0
  118. data/lib/enjine/mechanics/abilities/turn_rules.rb +49 -0
  119. data/lib/enjine/mechanics/card_spec.rb +177 -0
  120. data/lib/enjine/mechanics/copy_except.rb +73 -0
  121. data/lib/enjine/mechanics/costs/base.rb +115 -0
  122. data/lib/enjine/mechanics/costs/behold.rb +76 -0
  123. data/lib/enjine/mechanics/costs/discard.rb +65 -0
  124. data/lib/enjine/mechanics/costs/exert.rb +29 -0
  125. data/lib/enjine/mechanics/costs/exile_cards.rb +39 -0
  126. data/lib/enjine/mechanics/costs/from_graveyard.rb +45 -0
  127. data/lib/enjine/mechanics/costs/get_counters.rb +39 -0
  128. data/lib/enjine/mechanics/costs/mana.rb +37 -0
  129. data/lib/enjine/mechanics/costs/one_of.rb +49 -0
  130. data/lib/enjine/mechanics/costs/pay_life.rb +50 -0
  131. data/lib/enjine/mechanics/costs/remove_counters.rb +51 -0
  132. data/lib/enjine/mechanics/costs/sacrifice.rb +59 -0
  133. data/lib/enjine/mechanics/costs/tap.rb +56 -0
  134. data/lib/enjine/mechanics/costs/waterbend.rb +29 -0
  135. data/lib/enjine/mechanics/counters/kinds.rb +190 -0
  136. data/lib/enjine/mechanics/effects/add_subtypes.rb +30 -0
  137. data/lib/enjine/mechanics/effects/airbend.rb +41 -0
  138. data/lib/enjine/mechanics/effects/amass.rb +124 -0
  139. data/lib/enjine/mechanics/effects/assimilate.rb +72 -0
  140. data/lib/enjine/mechanics/effects/base.rb +148 -0
  141. data/lib/enjine/mechanics/effects/become.rb +182 -0
  142. data/lib/enjine/mechanics/effects/behold.rb +48 -0
  143. data/lib/enjine/mechanics/effects/blight.rb +94 -0
  144. data/lib/enjine/mechanics/effects/choose_cards.rb +168 -0
  145. data/lib/enjine/mechanics/effects/copy.rb +370 -0
  146. data/lib/enjine/mechanics/effects/counter_spell.rb +21 -0
  147. data/lib/enjine/mechanics/effects/counters.rb +161 -0
  148. data/lib/enjine/mechanics/effects/damage.rb +72 -0
  149. data/lib/enjine/mechanics/effects/delayed_triggers.rb +112 -0
  150. data/lib/enjine/mechanics/effects/destroy.rb +22 -0
  151. data/lib/enjine/mechanics/effects/discard.rb +86 -0
  152. data/lib/enjine/mechanics/effects/draw.rb +16 -0
  153. data/lib/enjine/mechanics/effects/earthbend.rb +70 -0
  154. data/lib/enjine/mechanics/effects/emblem.rb +52 -0
  155. data/lib/enjine/mechanics/effects/endure.rb +63 -0
  156. data/lib/enjine/mechanics/effects/exile.rb +49 -0
  157. data/lib/enjine/mechanics/effects/extra_turn.rb +122 -0
  158. data/lib/enjine/mechanics/effects/faces.rb +115 -0
  159. data/lib/enjine/mechanics/effects/fight.rb +76 -0
  160. data/lib/enjine/mechanics/effects/flip_coins.rb +97 -0
  161. data/lib/enjine/mechanics/effects/gain_control.rb +94 -0
  162. data/lib/enjine/mechanics/effects/game_end.rb +42 -0
  163. data/lib/enjine/mechanics/effects/library.rb +327 -0
  164. data/lib/enjine/mechanics/effects/life.rb +83 -0
  165. data/lib/enjine/mechanics/effects/look_at_hand.rb +17 -0
  166. data/lib/enjine/mechanics/effects/loot.rb +118 -0
  167. data/lib/enjine/mechanics/effects/mana.rb +96 -0
  168. data/lib/enjine/mechanics/effects/may.rb +82 -0
  169. data/lib/enjine/mechanics/effects/meld.rb +77 -0
  170. data/lib/enjine/mechanics/effects/mill.rb +22 -0
  171. data/lib/enjine/mechanics/effects/pay.rb +231 -0
  172. data/lib/enjine/mechanics/effects/phasing.rb +51 -0
  173. data/lib/enjine/mechanics/effects/proliferate.rb +72 -0
  174. data/lib/enjine/mechanics/effects/pump.rb +60 -0
  175. data/lib/enjine/mechanics/effects/put_in_library.rb +98 -0
  176. data/lib/enjine/mechanics/effects/return_choice.rb +29 -0
  177. data/lib/enjine/mechanics/effects/return_to_battlefield.rb +80 -0
  178. data/lib/enjine/mechanics/effects/return_to_hand.rb +20 -0
  179. data/lib/enjine/mechanics/effects/roll_dice.rb +45 -0
  180. data/lib/enjine/mechanics/effects/sacrifice.rb +118 -0
  181. data/lib/enjine/mechanics/effects/scry_and_surveil.rb +74 -0
  182. data/lib/enjine/mechanics/effects/search.rb +156 -0
  183. data/lib/enjine/mechanics/effects/tap_untap.rb +23 -0
  184. data/lib/enjine/mechanics/effects/tokens.rb +76 -0
  185. data/lib/enjine/mechanics/filters/any_target.rb +15 -0
  186. data/lib/enjine/mechanics/filters/base.rb +386 -0
  187. data/lib/enjine/mechanics/filters/graveyard.rb +23 -0
  188. data/lib/enjine/mechanics/filters/one_of.rb +55 -0
  189. data/lib/enjine/mechanics/filters/permanents.rb +52 -0
  190. data/lib/enjine/mechanics/filters/players.rb +20 -0
  191. data/lib/enjine/mechanics/filters/stack.rb +97 -0
  192. data/lib/enjine/mechanics/follow_up.rb +46 -0
  193. data/lib/enjine/mechanics/keywords/alternative_costs.rb +361 -0
  194. data/lib/enjine/mechanics/keywords/beam_me_up.rb +78 -0
  195. data/lib/enjine/mechanics/keywords/changeling.rb +58 -0
  196. data/lib/enjine/mechanics/keywords/convoke.rb +77 -0
  197. data/lib/enjine/mechanics/keywords/crew.rb +155 -0
  198. data/lib/enjine/mechanics/keywords/cycling.rb +58 -0
  199. data/lib/enjine/mechanics/keywords/decayed.rb +25 -0
  200. data/lib/enjine/mechanics/keywords/devour.rb +61 -0
  201. data/lib/enjine/mechanics/keywords/evergreen.rb +54 -0
  202. data/lib/enjine/mechanics/keywords/extort.rb +26 -0
  203. data/lib/enjine/mechanics/keywords/flags.rb +46 -0
  204. data/lib/enjine/mechanics/keywords/foretell.rb +45 -0
  205. data/lib/enjine/mechanics/keywords/gift.rb +50 -0
  206. data/lib/enjine/mechanics/keywords/hideaway.rb +28 -0
  207. data/lib/enjine/mechanics/keywords/increment.rb +31 -0
  208. data/lib/enjine/mechanics/keywords/job_select.rb +29 -0
  209. data/lib/enjine/mechanics/keywords/kicker.rb +92 -0
  210. data/lib/enjine/mechanics/keywords/mobilize_and_firebending.rb +84 -0
  211. data/lib/enjine/mechanics/keywords/paradigm.rb +91 -0
  212. data/lib/enjine/mechanics/keywords/power_up.rb +48 -0
  213. data/lib/enjine/mechanics/keywords/protection.rb +111 -0
  214. data/lib/enjine/mechanics/keywords/prowess.rb +30 -0
  215. data/lib/enjine/mechanics/keywords/riot.rb +35 -0
  216. data/lib/enjine/mechanics/keywords/saddle.rb +115 -0
  217. data/lib/enjine/mechanics/keywords/speed.rb +89 -0
  218. data/lib/enjine/mechanics/keywords/station.rb +163 -0
  219. data/lib/enjine/mechanics/keywords/storied.rb +41 -0
  220. data/lib/enjine/mechanics/keywords/storm_and_cascade.rb +86 -0
  221. data/lib/enjine/mechanics/keywords/sunburst.rb +29 -0
  222. data/lib/enjine/mechanics/keywords/suspend.rb +191 -0
  223. data/lib/enjine/mechanics/keywords/teamwork.rb +45 -0
  224. data/lib/enjine/mechanics/keywords/ward.rb +67 -0
  225. data/lib/enjine/mechanics/linked.rb +67 -0
  226. data/lib/enjine/mechanics/mana_spent.rb +99 -0
  227. data/lib/enjine/mechanics/selector.rb +207 -0
  228. data/lib/enjine/mechanics/subtypes/auras_and_equipment.rb +176 -0
  229. data/lib/enjine/mechanics/subtypes/basic_land_types.rb +36 -0
  230. data/lib/enjine/mechanics/subtypes/class_levels.rb +102 -0
  231. data/lib/enjine/mechanics/subtypes/saga.rb +89 -0
  232. data/lib/enjine/mechanics/supertypes/basic.rb +17 -0
  233. data/lib/enjine/mechanics/supertypes/legendary.rb +18 -0
  234. data/lib/enjine/mechanics/triggers/ability_triggers.rb +51 -0
  235. data/lib/enjine/mechanics/triggers/any_of.rb +43 -0
  236. data/lib/enjine/mechanics/triggers/base.rb +228 -0
  237. data/lib/enjine/mechanics/triggers/casting.rb +219 -0
  238. data/lib/enjine/mechanics/triggers/combat.rb +123 -0
  239. data/lib/enjine/mechanics/triggers/damage.rb +130 -0
  240. data/lib/enjine/mechanics/triggers/dilemma.rb +37 -0
  241. data/lib/enjine/mechanics/triggers/draw_and_discard.rb +85 -0
  242. data/lib/enjine/mechanics/triggers/keyword_actions.rb +55 -0
  243. data/lib/enjine/mechanics/triggers/life.rb +35 -0
  244. data/lib/enjine/mechanics/triggers/steps.rb +39 -0
  245. data/lib/enjine/mechanics/triggers/tap_and_counters.rb +111 -0
  246. data/lib/enjine/mechanics/triggers/zone_changes.rb +249 -0
  247. data/lib/enjine/mechanics/types/kindred.rb +30 -0
  248. data/lib/enjine/mechanics/types/land.rb +24 -0
  249. data/lib/enjine/mechanics/types/permanent_types.rb +67 -0
  250. data/lib/enjine/mechanics/types/spell_types.rb +38 -0
  251. data/lib/enjine/mechanics/values.rb +675 -0
  252. data/lib/enjine/mechanics.rb +74 -0
  253. data/lib/enjine/player.rb +30 -0
  254. data/lib/enjine/primitive.rb +61 -0
  255. data/lib/enjine/replacements.rb +191 -0
  256. data/lib/enjine/rng.rb +57 -0
  257. data/lib/enjine/rules_monitor.rb +404 -0
  258. data/lib/enjine/setup.rb +45 -0
  259. data/lib/enjine/snapshot/player_checks.rb +86 -0
  260. data/lib/enjine/snapshot/turn_checks.rb +91 -0
  261. data/lib/enjine/snapshot.rb +307 -0
  262. data/lib/enjine/state.rb +173 -0
  263. data/lib/enjine/steps.rb +117 -0
  264. data/lib/enjine/tokens.rb +25 -0
  265. data/lib/enjine/verify.rb +233 -0
  266. data/lib/enjine/version.rb +7 -0
  267. data/lib/enjine/view.rb +162 -0
  268. data/lib/enjine.rb +46 -0
  269. 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`.