From 1695e6767da013d144ba50a85dc9819d1bd6226c Mon Sep 17 00:00:00 2001 From: John Hitchings Date: Thu, 18 Jun 2020 23:45:02 -0700 Subject: [PATCH 1/4] add jumpstart swiss and elimiation tournament formats. --- .../client/dialog/NewTournamentDialog.java | 1 + .../java/mage/view/TournamentTypeView.java | 7 ++ .../JumpstartEliminationTournament.java | 44 ++++++++++ .../JumpstartEliminationTournamentType.java | 19 +++++ .../tournament/JumpstartSwissTournament.java | 44 ++++++++++ .../JumpstartSwissTournamentType.java | 18 ++++ Mage.Server/config/config.xml | 2 + .../jumpstart/JumpstartPoolGenerator.java | 84 +++++++++++++++++++ .../java/mage/game/jumpstart/jumpstart.txt | 15 ++++ .../mage/game/tournament/LimitedOptions.java | 12 ++- .../mage/game/tournament/TournamentImpl.java | 24 +++++- .../mage/game/tournament/TournamentType.java | 7 +- 12 files changed, 272 insertions(+), 5 deletions(-) create mode 100644 Mage.Server.Plugins/Mage.Tournament.Sealed/src/mage/tournament/JumpstartEliminationTournament.java create mode 100644 Mage.Server.Plugins/Mage.Tournament.Sealed/src/mage/tournament/JumpstartEliminationTournamentType.java create mode 100644 Mage.Server.Plugins/Mage.Tournament.Sealed/src/mage/tournament/JumpstartSwissTournament.java create mode 100644 Mage.Server.Plugins/Mage.Tournament.Sealed/src/mage/tournament/JumpstartSwissTournamentType.java create mode 100644 Mage/src/main/java/mage/game/jumpstart/JumpstartPoolGenerator.java create mode 100644 Mage/src/main/java/mage/game/jumpstart/jumpstart.txt diff --git a/Mage.Client/src/main/java/mage/client/dialog/NewTournamentDialog.java b/Mage.Client/src/main/java/mage/client/dialog/NewTournamentDialog.java index 648a13651a..2e4b980870 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/NewTournamentDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/NewTournamentDialog.java @@ -1203,6 +1203,7 @@ public class NewTournamentDialog extends MageDialog { tOptions.getLimitedOptions().setConstructionTime((Integer) this.spnConstructTime.getValue() * 60); tOptions.getLimitedOptions().setIsRandom(tournamentType.isRandom()); tOptions.getLimitedOptions().setIsRichMan(tournamentType.isRichMan()); + tOptions.getLimitedOptions().setIsJumpstart(tournamentType.isJumpstart()); if (tournamentType.isCubeBooster()) { tOptions.getLimitedOptions().setDraftCubeName(this.cbDraftCube.getSelectedItem().toString()); if (!(cubeFromDeckFilename.isEmpty())) { diff --git a/Mage.Common/src/main/java/mage/view/TournamentTypeView.java b/Mage.Common/src/main/java/mage/view/TournamentTypeView.java index 8bd4d8c3cc..22d7ecf9bc 100644 --- a/Mage.Common/src/main/java/mage/view/TournamentTypeView.java +++ b/Mage.Common/src/main/java/mage/view/TournamentTypeView.java @@ -22,6 +22,7 @@ public class TournamentTypeView implements Serializable { private final boolean elimination; private final boolean random; private final boolean richMan; + private final boolean jumpstart; public TournamentTypeView(TournamentType tournamentType) { this.name = tournamentType.getName(); @@ -34,6 +35,7 @@ public class TournamentTypeView implements Serializable { this.elimination = tournamentType.isElimination(); this.random = tournamentType.isRandom(); this.richMan = tournamentType.isRichMan(); + this.jumpstart = tournamentType.isJumpstart(); } @Override @@ -80,4 +82,9 @@ public class TournamentTypeView implements Serializable { public boolean isRichMan() { return richMan; } + + public boolean isJumpstart() { + return jumpstart; + } + } diff --git a/Mage.Server.Plugins/Mage.Tournament.Sealed/src/mage/tournament/JumpstartEliminationTournament.java b/Mage.Server.Plugins/Mage.Tournament.Sealed/src/mage/tournament/JumpstartEliminationTournament.java new file mode 100644 index 0000000000..d65634936a --- /dev/null +++ b/Mage.Server.Plugins/Mage.Tournament.Sealed/src/mage/tournament/JumpstartEliminationTournament.java @@ -0,0 +1,44 @@ + + +package mage.tournament; + +import mage.game.tournament.TournamentOptions; +import mage.game.tournament.TournamentSingleElimination; + +public class JumpstartEliminationTournament extends TournamentSingleElimination { + + protected enum TournamentStep { + START, OPEN_BOOSTERS, CONSTRUCT, COMPETE, WINNERS + } + + protected TournamentStep currentStep; + + public JumpstartEliminationTournament(TournamentOptions options) { + super(options); + currentStep = TournamentStep.START; + } + + @Override + public void nextStep() { + switch (currentStep) { + case START: + currentStep = TournamentStep.OPEN_BOOSTERS; + openBoosters(); + break; + case OPEN_BOOSTERS: + currentStep = TournamentStep.CONSTRUCT; + construct(); + break; + case CONSTRUCT: + currentStep = TournamentStep.COMPETE; + runTournament(); + break; + case COMPETE: + currentStep = TournamentStep.WINNERS; + winners(); + end(); + break; + } + } + +} diff --git a/Mage.Server.Plugins/Mage.Tournament.Sealed/src/mage/tournament/JumpstartEliminationTournamentType.java b/Mage.Server.Plugins/Mage.Tournament.Sealed/src/mage/tournament/JumpstartEliminationTournamentType.java new file mode 100644 index 0000000000..858588e441 --- /dev/null +++ b/Mage.Server.Plugins/Mage.Tournament.Sealed/src/mage/tournament/JumpstartEliminationTournamentType.java @@ -0,0 +1,19 @@ + + +package mage.tournament; + +import mage.game.tournament.TournamentType; + +public class JumpstartEliminationTournamentType extends TournamentType { + + public JumpstartEliminationTournamentType() { + this.name = "Jumpstart Elimination"; + this.maxPlayers = 16; + this.minPlayers = 2; + this.numBoosters = 0; + this.isJumpstart = true; + this.elimination = true; + this.limited = true; + } + +} diff --git a/Mage.Server.Plugins/Mage.Tournament.Sealed/src/mage/tournament/JumpstartSwissTournament.java b/Mage.Server.Plugins/Mage.Tournament.Sealed/src/mage/tournament/JumpstartSwissTournament.java new file mode 100644 index 0000000000..f0e7f34bc9 --- /dev/null +++ b/Mage.Server.Plugins/Mage.Tournament.Sealed/src/mage/tournament/JumpstartSwissTournament.java @@ -0,0 +1,44 @@ + + +package mage.tournament; + +import mage.game.tournament.TournamentOptions; +import mage.game.tournament.TournamentSwiss; + +public class JumpstartSwissTournament extends TournamentSwiss { + + protected enum TournamentStep { + START, OPEN_BOOSTERS, CONSTRUCT, COMPETE, WINNERS + } + + protected TournamentStep currentStep; + + public JumpstartSwissTournament(TournamentOptions options) { + super(options); + currentStep = TournamentStep.START; + } + + @Override + public void nextStep() { + switch (currentStep) { + case START: + currentStep = TournamentStep.OPEN_BOOSTERS; + openBoosters(); + break; + case OPEN_BOOSTERS: + currentStep = TournamentStep.CONSTRUCT; + construct(); + break; + case CONSTRUCT: + currentStep = TournamentStep.COMPETE; + runTournament(); + break; + case COMPETE: + currentStep = TournamentStep.WINNERS; + winners(); + end(); + break; + } + } + +} diff --git a/Mage.Server.Plugins/Mage.Tournament.Sealed/src/mage/tournament/JumpstartSwissTournamentType.java b/Mage.Server.Plugins/Mage.Tournament.Sealed/src/mage/tournament/JumpstartSwissTournamentType.java new file mode 100644 index 0000000000..bf5328ed52 --- /dev/null +++ b/Mage.Server.Plugins/Mage.Tournament.Sealed/src/mage/tournament/JumpstartSwissTournamentType.java @@ -0,0 +1,18 @@ + + +package mage.tournament; + +import mage.game.tournament.TournamentType; + +public class JumpstartSwissTournamentType extends TournamentType { + + public JumpstartSwissTournamentType() { + this.name = "Jumpstart Swiss"; + this.maxPlayers = 16; + this.minPlayers = 2; + this.numBoosters = 0; + this.isJumpstart = true; + this.limited = true; + } + +} diff --git a/Mage.Server/config/config.xml b/Mage.Server/config/config.xml index 28ffc64abf..6ee2e09546 100644 --- a/Mage.Server/config/config.xml +++ b/Mage.Server/config/config.xml @@ -104,6 +104,8 @@ + + diff --git a/Mage/src/main/java/mage/game/jumpstart/JumpstartPoolGenerator.java b/Mage/src/main/java/mage/game/jumpstart/JumpstartPoolGenerator.java new file mode 100644 index 0000000000..476f2b7337 --- /dev/null +++ b/Mage/src/main/java/mage/game/jumpstart/JumpstartPoolGenerator.java @@ -0,0 +1,84 @@ +package mage.game.jumpstart; + +import java.io.File; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +import com.google.common.base.Charsets; +import com.google.common.io.CharSource; +import com.google.common.io.Files; +import com.google.common.io.Resources; + +import mage.cards.Card; +import mage.cards.decks.Deck; +import mage.cards.decks.DeckCardInfo; +import mage.cards.decks.DeckCardLists; +import mage.game.GameException; + +public class JumpstartPoolGenerator { + + private static final String RESOURCE_NAME = "mage/game/jumpstart/jumpstart.txt"; + private static final List JUMPSTART_PACKS; + + static { + try { + CharSource source = Resources.asCharSource(Resources.getResource(RESOURCE_NAME), Charsets.UTF_8); + List packs = new ArrayList<>(); + JumpstartPack pack = new JumpstartPack(); + for (String line : source.readLines()) { + if (line.isEmpty()) { + if (!pack.isEmpty()) { + packs.add(pack); + pack = new JumpstartPack(); + } + } else if (line.startsWith("#")) { + // skip comment + } else { + String[] ls = line.split(" ", 3); + pack.add(new DeckCardInfo(ls[2], ls[1], ls[0])); + } + } + JUMPSTART_PACKS = Collections.unmodifiableList(packs); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + public static Set generatePool() { + try { + DeckCardLists list = new DeckCardLists(); + SecureRandom random = new SecureRandom(); + for (int i = 0; i < 2; i++) { + int index = random.nextInt(JUMPSTART_PACKS.size()); + list.getCards().addAll(JUMPSTART_PACKS.get(index).getCards()); + } + return Deck.load(list).getCards(); + } catch (GameException e) { + throw new RuntimeException(e); + } + } + + public static class JumpstartPack { + + private final List cards = new ArrayList<>(); + + public void add(DeckCardInfo card) { + cards.add(card); + } + + public boolean isEmpty() { + return cards.isEmpty(); + } + + public List getCards() { + return Collections.unmodifiableList(cards); + } + + } + +} diff --git a/Mage/src/main/java/mage/game/jumpstart/jumpstart.txt b/Mage/src/main/java/mage/game/jumpstart/jumpstart.txt new file mode 100644 index 0000000000..cd0f7fad66 --- /dev/null +++ b/Mage/src/main/java/mage/game/jumpstart/jumpstart.txt @@ -0,0 +1,15 @@ +# setCode cardNum cardName +IKO 105 Whisper Squad +IKO 105 Whisper Squad +IKO 105 Whisper Squad +IKO 105 Whisper Squad + +IKO 113 Drannith Stinger +IKO 113 Drannith Stinger +IKO 113 Drannith Stinger +IKO 113 Drannith Stinger + +IKO 149 Essence Symbiote +IKO 149 Essence Symbiote +IKO 149 Essence Symbiote +IKO 149 Essence Symbiote diff --git a/Mage/src/main/java/mage/game/tournament/LimitedOptions.java b/Mage/src/main/java/mage/game/tournament/LimitedOptions.java index 0151beed80..9628b5c33a 100644 --- a/Mage/src/main/java/mage/game/tournament/LimitedOptions.java +++ b/Mage/src/main/java/mage/game/tournament/LimitedOptions.java @@ -19,7 +19,8 @@ public class LimitedOptions implements Serializable { protected int numberBoosters; protected boolean isRandom; protected boolean isRichMan; - protected Deck cubeFromDeck = null; + protected Deck cubeFromDeck; + protected boolean isJumpstart; public List getSetCodes() { return sets; @@ -80,4 +81,13 @@ public class LimitedOptions implements Serializable { public void setIsRichMan(boolean isRichMan) { this.isRichMan = isRichMan; } + + public void setIsJumpstart(boolean isJumpstart) { + this.isJumpstart = isJumpstart; + } + + public boolean getIsJumpstart() { + return this.isJumpstart; + } + } diff --git a/Mage/src/main/java/mage/game/tournament/TournamentImpl.java b/Mage/src/main/java/mage/game/tournament/TournamentImpl.java index 016ca73dd6..cd3729b844 100644 --- a/Mage/src/main/java/mage/game/tournament/TournamentImpl.java +++ b/Mage/src/main/java/mage/game/tournament/TournamentImpl.java @@ -1,15 +1,32 @@ package mage.game.tournament; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; import java.util.concurrent.CopyOnWriteArrayList; + +import org.apache.log4j.Logger; + import mage.cards.ExpansionSet; import mage.cards.decks.Deck; import mage.constants.TournamentPlayerState; import mage.game.draft.Draft; import mage.game.draft.DraftCube; -import mage.game.events.*; +import mage.game.events.Listener; +import mage.game.events.PlayerQueryEvent; +import mage.game.events.PlayerQueryEventSource; +import mage.game.events.TableEvent; import mage.game.events.TableEvent.EventType; +import mage.game.events.TableEventSource; +import mage.game.jumpstart.JumpstartPoolGenerator; import mage.game.match.Match; import mage.game.match.MatchPlayer; import mage.game.result.ResultProtos.MatchPlayerProto; @@ -20,7 +37,6 @@ import mage.game.result.ResultProtos.TourneyRoundProto; import mage.players.Player; import mage.players.PlayerType; import mage.util.RandomUtil; -import org.apache.log4j.Logger; /** * @@ -394,6 +410,8 @@ public abstract class TournamentImpl implements Tournament { for (int i = 0; i < options.getLimitedOptions().getNumberBoosters(); i++) { player.getDeck().getSideboard().addAll(cube.createBooster()); } + } else if (options.getLimitedOptions().getIsJumpstart()) { + player.getDeck().getSideboard().addAll(JumpstartPoolGenerator.generatePool()); } else { for (ExpansionSet set : sets) { player.getDeck().getSideboard().addAll(set.createBooster()); diff --git a/Mage/src/main/java/mage/game/tournament/TournamentType.java b/Mage/src/main/java/mage/game/tournament/TournamentType.java index 5b227ed3d1..f11bd7e38e 100644 --- a/Mage/src/main/java/mage/game/tournament/TournamentType.java +++ b/Mage/src/main/java/mage/game/tournament/TournamentType.java @@ -18,7 +18,8 @@ public class TournamentType implements Serializable { protected boolean limited; // or construced protected boolean elimination; // or Swiss protected boolean isRandom; - protected boolean isRichMan = false; // or Rich Man Draft + protected boolean isRichMan; // or Rich Man Draft + protected boolean isJumpstart; protected TournamentType() { } @@ -68,4 +69,8 @@ public class TournamentType implements Serializable { return this.isRichMan; } + public boolean isJumpstart() { + return this.isJumpstart; + } + } From 1bd98d64357cbc64cda208940a493b087601f371 Mon Sep 17 00:00:00 2001 From: John Hitchings Date: Fri, 19 Jun 2020 00:04:09 -0700 Subject: [PATCH 2/4] move jumpstart file to resources --- .../jumpstart/JumpstartPoolGenerator.java | 4 +- .../java/mage/game/jumpstart/jumpstart.txt | 15 ------ .../main/resources/jumpstart/jumpstart.txt | 50 +++++++++++++++++++ 3 files changed, 51 insertions(+), 18 deletions(-) delete mode 100644 Mage/src/main/java/mage/game/jumpstart/jumpstart.txt create mode 100644 Mage/src/main/resources/jumpstart/jumpstart.txt diff --git a/Mage/src/main/java/mage/game/jumpstart/JumpstartPoolGenerator.java b/Mage/src/main/java/mage/game/jumpstart/JumpstartPoolGenerator.java index 476f2b7337..b9425d2c3a 100644 --- a/Mage/src/main/java/mage/game/jumpstart/JumpstartPoolGenerator.java +++ b/Mage/src/main/java/mage/game/jumpstart/JumpstartPoolGenerator.java @@ -1,6 +1,5 @@ package mage.game.jumpstart; -import java.io.File; import java.io.IOException; import java.io.UncheckedIOException; import java.security.SecureRandom; @@ -11,7 +10,6 @@ import java.util.Set; import com.google.common.base.Charsets; import com.google.common.io.CharSource; -import com.google.common.io.Files; import com.google.common.io.Resources; import mage.cards.Card; @@ -22,7 +20,7 @@ import mage.game.GameException; public class JumpstartPoolGenerator { - private static final String RESOURCE_NAME = "mage/game/jumpstart/jumpstart.txt"; + private static final String RESOURCE_NAME = "jumpstart/jumpstart.txt"; private static final List JUMPSTART_PACKS; static { diff --git a/Mage/src/main/java/mage/game/jumpstart/jumpstart.txt b/Mage/src/main/java/mage/game/jumpstart/jumpstart.txt deleted file mode 100644 index cd0f7fad66..0000000000 --- a/Mage/src/main/java/mage/game/jumpstart/jumpstart.txt +++ /dev/null @@ -1,15 +0,0 @@ -# setCode cardNum cardName -IKO 105 Whisper Squad -IKO 105 Whisper Squad -IKO 105 Whisper Squad -IKO 105 Whisper Squad - -IKO 113 Drannith Stinger -IKO 113 Drannith Stinger -IKO 113 Drannith Stinger -IKO 113 Drannith Stinger - -IKO 149 Essence Symbiote -IKO 149 Essence Symbiote -IKO 149 Essence Symbiote -IKO 149 Essence Symbiote diff --git a/Mage/src/main/resources/jumpstart/jumpstart.txt b/Mage/src/main/resources/jumpstart/jumpstart.txt new file mode 100644 index 0000000000..f030253772 --- /dev/null +++ b/Mage/src/main/resources/jumpstart/jumpstart.txt @@ -0,0 +1,50 @@ +# setCode cardNum cardName +# Red Cards +ELD 137 Rimrock Knight +ELD 137 Rimrock Knight +ELD 137 Rimrock Knight +ELD 137 Rimrock Knight +ELD 291 Bonecrusher Giant +ELD 291 Bonecrusher Giant +ELD 291 Bonecrusher Giant +ELD 291 Bonecrusher Giant +ELD 120 Embercleave +ELD 120 Embercleave +ELD 120 Embercleave +ELD 120 Embercleave + +# Green Cards +GRN 141 Pelt Collector +GRN 141 Pelt Collector +GRN 141 Pelt Collector +GRN 141 Pelt Collector +WAR 171 Paradise Druid +WAR 171 Paradise Druid +WAR 171 Paradise Druid +WAR 171 Paradise Druid +ELD 165 Lovestruck Beast +ELD 165 Lovestruck Beast +ELD 165 Lovestruck Beast +ELD 165 Lovestruck Beast +ELD 171 Questing Beast +ELD 171 Questing Beast +ELD 171 Questing Beast +ELD 171 Questing Beast + +# Black Cards +THB 123 Woe Strider +THB 123 Woe Strider +THB 123 Woe Strider +THB 123 Woe Strider +ELD 81 Cauldron Familiar +ELD 81 Cauldron Familiar +ELD 81 Cauldron Familiar +ELD 81 Cauldron Familiar +ELD 237 Witch's Oven +ELD 237 Witch's Oven +ELD 237 Witch's Oven +ELD 237 Witch's Oven +IKO 220 Fiend Artisan +IKO 220 Fiend Artisan +IKO 220 Fiend Artisan +IKO 220 Fiend Artisan \ No newline at end of file From c54c100b4dbd9d401c7ac8f00e614c299ff4e12a Mon Sep 17 00:00:00 2001 From: John Hitchings Date: Fri, 19 Jun 2020 22:10:09 -0700 Subject: [PATCH 3/4] add docs/links for jumpstart, tweak the jumpstart packs file. --- Mage.Server/release/config/config.xml | 2 + .../jumpstart/JumpstartPoolGenerator.java | 40 +++++++++++- .../mage/game/tournament/TournamentImpl.java | 2 +- .../main/resources/jumpstart/jumpstart.txt | 63 ++++++------------- 4 files changed, 58 insertions(+), 49 deletions(-) diff --git a/Mage.Server/release/config/config.xml b/Mage.Server/release/config/config.xml index aac80217ba..3a9810f386 100644 --- a/Mage.Server/release/config/config.xml +++ b/Mage.Server/release/config/config.xml @@ -98,6 +98,8 @@ + + diff --git a/Mage/src/main/java/mage/game/jumpstart/JumpstartPoolGenerator.java b/Mage/src/main/java/mage/game/jumpstart/JumpstartPoolGenerator.java index b9425d2c3a..9039e28e07 100644 --- a/Mage/src/main/java/mage/game/jumpstart/JumpstartPoolGenerator.java +++ b/Mage/src/main/java/mage/game/jumpstart/JumpstartPoolGenerator.java @@ -20,6 +20,36 @@ import mage.game.GameException; public class JumpstartPoolGenerator { + /** + * "Jumpstart is a new way to play Magic that mashes together themes from throughout the history of the game and + * lets you skip the deckbuilding part. Grab two boosters, shuffle them into a 40-card deck, and start playing. + * The set contains almost 500 reprints but also introduces 37 new cards that were designed to help fill out some + * of the themes. These are not going to be Standard-, Pioneer-, or Modern-legal cards, but are legal in Eternal + * formats (Legacy, Vintage, Pauper and Commander). There are no foil cards in the set. 120 cards are from Core + * Set 2021[7], and more than 400 reprints are from sets before Core Set 2021. Cards from M21 have the M21 expansion + * symbol, while new cards and other reprints will have the Jumpstart symbol. Reprints have a Post-M15 card frame. + * The new cards and cards with new art are numbered #1/78 to #78/78 (40 basic lands, TBA common, TBA uncommon, + * TBA rare, and TBA mythic rare). The other reprints with the Jumpstart expansion symbol are numbered #79 to 495." + * + * "Jumpstart is sold in 20-card boosters, and booster boxes contain 24 boosters.[9] All 20 cards in a booster fit a + * theme, and most themes are mono-color and have multiple variations of cards included in the pack, totaling 121 + * different possible pack contents. Special "Mythic Rare" packs don't have variations at all, but just one possible + * card list and can feature multiple colors. What theme each booster contains is randomized. Most packs are + * singletons, but there are some instances of having two copies of a card in the pack. All boosters contain at + * least one rare, and one in three boosters includes an extra rare. Of the 20 cards in a pack, seven or eight + * are lands. One basic land (or in the case of Rainbow, Terramorphic Expanse) features art that matches the pack's + * theme. The planeswalker themed packs use the respective Showcase lands from M21, but the other packs use brand-new + * themed land art created for the set. The booster pack is wrapped like a regular pack, but the set of cards is + * packed in an additional plastic wrap, with a face card (the "Pack Summary card") that indicates the theme and + * the color of the half-deck." + * + * Source: https://mtg.gamepedia.com/Jumpstart + * + * Announcement: https://magic.wizards.com/en/articles/archive/news/introducing-jumpstart-new-way-play-magic-2020-02-20 + * Card Pool: https://magic.wizards.com/en/articles/archive/card-image-gallery/jumpstart + * Deck Lists: https://magic.wizards.com/en/articles/archive/feature/jumpstart-decklists-2020-06-18 + */ + private static final String RESOURCE_NAME = "jumpstart/jumpstart.txt"; private static final List JUMPSTART_PACKS; @@ -28,17 +58,21 @@ public class JumpstartPoolGenerator { CharSource source = Resources.asCharSource(Resources.getResource(RESOURCE_NAME), Charsets.UTF_8); List packs = new ArrayList<>(); JumpstartPack pack = new JumpstartPack(); + packs.add(pack); for (String line : source.readLines()) { if (line.isEmpty()) { if (!pack.isEmpty()) { - packs.add(pack); pack = new JumpstartPack(); + packs.add(pack); } } else if (line.startsWith("#")) { // skip comment } else { - String[] ls = line.split(" ", 3); - pack.add(new DeckCardInfo(ls[2], ls[1], ls[0])); + String[] ls = line.split(" ", 4); + int quantity = Integer.parseInt(ls[0]); + for (int i = 0; i < quantity; i++) { + pack.add(new DeckCardInfo(ls[3], ls[2], ls[1])); + } } } JUMPSTART_PACKS = Collections.unmodifiableList(packs); diff --git a/Mage/src/main/java/mage/game/tournament/TournamentImpl.java b/Mage/src/main/java/mage/game/tournament/TournamentImpl.java index cd3729b844..001cf9dc6e 100644 --- a/Mage/src/main/java/mage/game/tournament/TournamentImpl.java +++ b/Mage/src/main/java/mage/game/tournament/TournamentImpl.java @@ -411,7 +411,7 @@ public abstract class TournamentImpl implements Tournament { player.getDeck().getSideboard().addAll(cube.createBooster()); } } else if (options.getLimitedOptions().getIsJumpstart()) { - player.getDeck().getSideboard().addAll(JumpstartPoolGenerator.generatePool()); + player.getDeck().getCards().addAll(JumpstartPoolGenerator.generatePool()); } else { for (ExpansionSet set : sets) { player.getDeck().getSideboard().addAll(set.createBooster()); diff --git a/Mage/src/main/resources/jumpstart/jumpstart.txt b/Mage/src/main/resources/jumpstart/jumpstart.txt index f030253772..e9e6ddb0d6 100644 --- a/Mage/src/main/resources/jumpstart/jumpstart.txt +++ b/Mage/src/main/resources/jumpstart/jumpstart.txt @@ -1,50 +1,23 @@ -# setCode cardNum cardName +# Jumpstart Decklists +# https://magic.wizards.com/en/articles/archive/feature/jumpstart-decklists-2020-06-18 +# quantity setCode cardNum cardName + # Red Cards -ELD 137 Rimrock Knight -ELD 137 Rimrock Knight -ELD 137 Rimrock Knight -ELD 137 Rimrock Knight -ELD 291 Bonecrusher Giant -ELD 291 Bonecrusher Giant -ELD 291 Bonecrusher Giant -ELD 291 Bonecrusher Giant -ELD 120 Embercleave -ELD 120 Embercleave -ELD 120 Embercleave -ELD 120 Embercleave +3 ELD 137 Rimrock Knight +4 ELD 115 Bonecrusher Giant +2 ELD 120 Embercleave +8 ELD 262 Mountain # Green Cards -GRN 141 Pelt Collector -GRN 141 Pelt Collector -GRN 141 Pelt Collector -GRN 141 Pelt Collector -WAR 171 Paradise Druid -WAR 171 Paradise Druid -WAR 171 Paradise Druid -WAR 171 Paradise Druid -ELD 165 Lovestruck Beast -ELD 165 Lovestruck Beast -ELD 165 Lovestruck Beast -ELD 165 Lovestruck Beast -ELD 171 Questing Beast -ELD 171 Questing Beast -ELD 171 Questing Beast -ELD 171 Questing Beast +3 GRN 141 Pelt Collector +3 WAR 171 Paradise Druid +3 ELD 165 Lovestruck Beast +3 ELD 171 Questing Beast +8 ELD 266 Forest # Black Cards -THB 123 Woe Strider -THB 123 Woe Strider -THB 123 Woe Strider -THB 123 Woe Strider -ELD 81 Cauldron Familiar -ELD 81 Cauldron Familiar -ELD 81 Cauldron Familiar -ELD 81 Cauldron Familiar -ELD 237 Witch's Oven -ELD 237 Witch's Oven -ELD 237 Witch's Oven -ELD 237 Witch's Oven -IKO 220 Fiend Artisan -IKO 220 Fiend Artisan -IKO 220 Fiend Artisan -IKO 220 Fiend Artisan \ No newline at end of file +2 THB 123 Woe Strider +4 ELD 81 Cauldron Familiar +3 ELD 237 Witch's Oven +3 IKO 220 Fiend Artisan +8 ELD 258 Swamp From 426f207aac700f0d510a952393d046eec1904f75 Mon Sep 17 00:00:00 2001 From: John Hitchings Date: Mon, 22 Jun 2020 12:25:18 -0700 Subject: [PATCH 4/4] add final jumpstart card pool and additional comments on JumpstartPoolGenerator --- .../jumpstart/JumpstartPoolGenerator.java | 13 + .../main/resources/jumpstart/jumpstart.txt | 1231 ++++++++++++++++- 2 files changed, 1224 insertions(+), 20 deletions(-) diff --git a/Mage/src/main/java/mage/game/jumpstart/JumpstartPoolGenerator.java b/Mage/src/main/java/mage/game/jumpstart/JumpstartPoolGenerator.java index 9039e28e07..e69f0252ce 100644 --- a/Mage/src/main/java/mage/game/jumpstart/JumpstartPoolGenerator.java +++ b/Mage/src/main/java/mage/game/jumpstart/JumpstartPoolGenerator.java @@ -81,6 +81,19 @@ public class JumpstartPoolGenerator { } } + /* Notes + * + * 1) the pools generated by this method are for how the prerelease will be played (see https://mtg.gamepedia.com/Jumpstart#Events) + * In order to support the 4 pack version, xmage would need to support editing multiple decks and some Duo format + * similar to https://mtg.gamepedia.com/Duo_Standard + * + * 2) this treats all packs to have similar chance to open (packs will actually be opened at a particular rarity). This + * could be implemented if you know the various ratios between packs by knowing the rarities from this source: + * https://mtg.gamepedia.com/Jumpstart#Themes_and_mechanics + * + * 3) this does not attempt to add an additional rare 1/3 of the time as described here: + * https://mtg.gamepedia.com/Jumpstart#Marketing + */ public static Set generatePool() { try { DeckCardLists list = new DeckCardLists(); diff --git a/Mage/src/main/resources/jumpstart/jumpstart.txt b/Mage/src/main/resources/jumpstart/jumpstart.txt index e9e6ddb0d6..73df97d972 100644 --- a/Mage/src/main/resources/jumpstart/jumpstart.txt +++ b/Mage/src/main/resources/jumpstart/jumpstart.txt @@ -1,23 +1,1214 @@ -# Jumpstart Decklists -# https://magic.wizards.com/en/articles/archive/feature/jumpstart-decklists-2020-06-18 -# quantity setCode cardNum cardName +# Archaeology (1) +1 JMP 471 Juggernaut +1 JMP 474 Meteor Golem +1 JMP 458 Ancestral Statue +1 JMP 150 Erratic Visionary +1 JMP 183 Thirst for Knowledge +1 JMP 462 Chromatic Sphere +1 JMP 468 Hedron Archive +1 JMP 491 Buried Ruin +5 JMP 50 Island +1 JMP 14 Scholar of the Lost Trove +1 JMP 456 Aether Spellbomb +2 JMP 9 Archaeomender +1 JMP 32 Lightning-Core Excavator +1 JMP 36 Thriving Isle +1 JMP 49 Island -# Red Cards -3 ELD 137 Rimrock Knight -4 ELD 115 Bonecrusher Giant -2 ELD 120 Embercleave -8 ELD 262 Mountain +# Archaeology (2) +1 JMP 471 Juggernaut +1 JMP 474 Meteor Golem +1 JMP 458 Ancestral Statue +1 JMP 150 Erratic Visionary +1 JMP 183 Thirst for Knowledge +1 JMP 464 Dreamstone Hedron +1 JMP 488 Terrarion +1 JMP 491 Buried Ruin +5 JMP 50 Island +1 JMP 14 Scholar of the Lost Trove +1 JMP 456 Aether Spellbomb +2 JMP 9 Archaeomender +1 JMP 32 Lightning-Core Excavator +1 JMP 36 Thriving Isle +1 JMP 49 Island -# Green Cards -3 GRN 141 Pelt Collector -3 WAR 171 Paradise Druid -3 ELD 165 Lovestruck Beast -3 ELD 171 Questing Beast -8 ELD 266 Forest +# Archaeology (3) +1 JMP 176 Sharding Sphinx +1 JMP 187 Vedalken Archmage +1 JMP 471 Juggernaut +1 JMP 474 Meteor Golem +1 JMP 458 Ancestral Statue +1 M21 235 Prismite +1 JMP 183 Thirst for Knowledge +1 JMP 468 Hedron Archive +1 JMP 491 Buried Ruin +5 JMP 50 Island +1 JMP 456 Aether Spellbomb +2 JMP 9 Archaeomender +1 JMP 32 Lightning-Core Excavator +1 JMP 36 Thriving Isle +1 JMP 49 Island -# Black Cards -2 THB 123 Woe Strider -4 ELD 81 Cauldron Familiar -3 ELD 237 Witch's Oven -3 IKO 220 Fiend Artisan -8 ELD 258 Swamp +# Archaeology (4) +1 JMP 482 Scarecrone +1 JMP 471 Juggernaut +1 JMP 474 Meteor Golem +1 JMP 484 Scuttlemutt +1 JMP 470 Jousting Dummy +1 JMP 485 Signpost Scarecrow +1 JMP 183 Thirst for Knowledge +1 M21 236 Short Sword +1 JMP 491 Buried Ruin +5 JMP 50 Island +1 JMP 456 Aether Spellbomb +2 JMP 9 Archaeomender +1 JMP 32 Lightning-Core Excavator +1 JMP 36 Thriving Isle +1 JMP 49 Island + +# Basri +1 M21 12 Concordia Pegasus +1 JMP 114 Knight of the Tusk +1 M21 24 Legion's Judgment +1 M21 19 Feat of Resistance +1 M21 17 Faith's Fetters +7 JMP 45 Plains +1 JMP 35 Thriving Heath +1 M21 280 Basri Ket +1 M21 9 Basri's Lieutenant +1 M21 41 Tempered Veteran +1 M21 37 Siege Striker +1 M21 10 Basri's Solidarity +1 M21 39 Staunch Shieldmate +1 M21 287 Basri's Acolyte + +# Cats (1) +1 JMP 407 Keeper of Fables +1 JMP 403 Initiate's Companion +1 JMP 419 Pouncing Cheetah +1 JMP 397 Feral Prowler +1 JMP 392 Enlarge +1 JMP 412 Nature's Way +1 JMP 386 Crushing Canopy +1 JMP 396 Feral Invocation +6 JMP 74 Forest +1 JMP 34 Thriving Grove +1 JMP 74 Forest +1 M21 374 Feline Sovereign +1 M21 175 Canopy Stalker +1 M21 202 Sabertooth Mauler +1 M21 196 Pridemalkin + +# Cats (2) +1 JMP 407 Keeper of Fables +1 JMP 403 Initiate's Companion +1 JMP 418 Penumbra Bobcat +1 JMP 397 Feral Prowler +1 JMP 392 Enlarge +1 JMP 412 Nature's Way +1 JMP 386 Crushing Canopy +1 JMP 410 Lurking Predators +1 JMP 396 Feral Invocation +6 JMP 74 Forest +1 JMP 34 Thriving Grove +1 JMP 74 Forest +1 M21 374 Feline Sovereign +1 M21 175 Canopy Stalker +1 M21 196 Pridemalkin + +# Chandra +1 JMP 356 Pyroclastic Elemental +1 JMP 372 Young Pyromancer +1 JMP 315 Fanatical Firebrand +1 JMP 317 Flames of the Firebrand +1 JMP 355 Pillar of Flame +1 JMP 336 Hungry Flames +1 M21 165 Thrill of Possibility +7 JMP 64 Mountain +1 JMP 33 Thriving Bluff +1 M21 135 Chandra, Heart of Fire +1 M21 136 Chandra's Incinerator +1 M21 138 Chandra's Pyreling +2 M21 303 Chandra's Magmutt + +# Dinosaurs (1) +1 JMP 399 Ghalta, Primal Hunger +1 JMP 388 Drover of the Mighty +1 JMP 416 Orazca Frillback +1 M21 209 Thrashing Brontodon +1 M21 176 Colossal Dreadmaw +1 JMP 427 Savage Stomp +1 JMP 384 Commune with Dinosaurs +1 M21 177 Cultivate +1 JMP 386 Crushing Canopy +1 M21 205 Setessan Training +6 JMP 74 Forest +1 JMP 34 Thriving Grove +1 JMP 73 Forest +1 M21 194 Ornery Dilophosaur +1 M21 178 Drowsing Tyrannodon + +# Dinosaurs (2) +1 JMP 429 Selvala, Heart of the Wilds +1 JMP 388 Drover of the Mighty +1 JMP 437 Thundering Spineback +1 JMP 416 Orazca Frillback +1 M21 176 Colossal Dreadmaw +1 JMP 427 Savage Stomp +1 JMP 384 Commune with Dinosaurs +1 JMP 386 Crushing Canopy +1 JMP 414 New Horizons +6 JMP 74 Forest +1 JMP 34 Thriving Grove +1 JMP 73 Forest +1 M21 308 Garruk's Uprising +1 M21 194 Ornery Dilophosaur +1 M21 178 Drowsing Tyrannodon + +# Dinosaurs (3) +1 JMP 399 Ghalta, Primal Hunger +1 JMP 388 Drover of the Mighty +1 JMP 437 Thundering Spineback +1 JMP 416 Orazca Frillback +1 M21 176 Colossal Dreadmaw +1 JMP 427 Savage Stomp +1 JMP 384 Commune with Dinosaurs +1 M21 177 Cultivate +1 JMP 386 Crushing Canopy +1 M21 210 Titanic Growth +6 JMP 74 Forest +1 JMP 34 Thriving Grove +1 JMP 73 Forest +1 M21 194 Ornery Dilophosaur +1 M21 178 Drowsing Tyrannodon + +# Dinosaurs (4) +1 JMP 388 Drover of the Mighty +1 JMP 416 Orazca Frillback +1 M21 209 Thrashing Brontodon +1 M21 176 Colossal Dreadmaw +1 JMP 427 Savage Stomp +1 JMP 384 Commune with Dinosaurs +1 JMP 414 New Horizons +6 JMP 74 Forest +1 JMP 423 Rampaging Brontodon +1 JMP 34 Thriving Grove +1 JMP 73 Forest +1 M21 377 Primal Might +1 M21 308 Garruk's Uprising +1 M21 194 Ornery Dilophosaur +1 M21 178 Drowsing Tyrannodon + +# Discarding 1 +1 JMP 233 Fell Specter +1 JMP 269 Ravenous Chupacabra +1 JMP 227 Entomber Exarch +1 JMP 214 Burglar Rat +1 JMP 266 Phyrexian Rager +1 JMP 279 Slate Street Ruffian +2 JMP 287 Wight of Precinct Six +1 JMP 200 Assassin's Strike +1 M21 115 Mind Rot +1 JMP 222 Death's Approach +7 JMP 57 Swamp +1 JMP 17 Tinybones, Trinket Thief +1 JMP 37 Thriving Moor + +# Discarding 2 +1 JMP 251 Liliana's Reaver +1 JMP 259 Nyxathid +1 JMP 233 Fell Specter +1 JMP 269 Ravenous Chupacabra +1 JMP 227 Entomber Exarch +1 JMP 214 Burglar Rat +1 JMP 266 Phyrexian Rager +1 JMP 279 Slate Street Ruffian +2 JMP 287 Wight of Precinct Six +1 JMP 200 Assassin's Strike +1 JMP 222 Death's Approach +7 JMP 57 Swamp +1 JMP 37 Thriving Moor + +# Dogs (1) +1 JMP 81 Affa Guard Hound +1 JMP 94 Cathar's Companion +1 M21 19 Feat of Resistance +1 JMP 125 Pacifism +6 JMP 45 Plains +1 JMP 4 Release the Dogs +1 JMP 7 Supply Runners +1 JMP 8 Trusty Retriever +1 JMP 35 Thriving Heath +1 JMP 45 Plains +1 M21 29 Pack Leader +1 M21 36 Selfless Savior +1 M21 35 Secure the Scene +1 M21 2 Alpine Watchdog +1 M21 30 Rambunctious Mutt + +# Dogs (2) +1 JMP 113 Isamaru, Hound of Konda +1 JMP 81 Affa Guard Hound +1 JMP 94 Cathar's Companion +1 JMP 99 Dauntless Onslaught +1 JMP 125 Pacifism +6 JMP 45 Plains +1 JMP 4 Release the Dogs +1 JMP 7 Supply Runners +1 JMP 8 Trusty Retriever +1 JMP 35 Thriving Heath +1 JMP 45 Plains +1 M21 29 Pack Leader +1 M21 35 Secure the Scene +1 M21 2 Alpine Watchdog +1 M21 30 Rambunctious Mutt + +# Elves (1) +1 JMP 385 Craterhoof Behemoth +1 JMP 391 Elvish Archdruid +1 JMP 389 Dwynen's Elite +1 JMP 400 Ghirapur Guide +1 JMP 430 Silhana Wayfinder +1 JMP 444 Wildheart Invoker +1 JMP 386 Crushing Canopy +1 M21 210 Titanic Growth +1 JMP 420 Presence of Gond +6 JMP 74 Forest +1 JMP 34 Thriving Grove +1 JMP 77 Forest +1 M21 206 Skyway Sniper +1 M21 193 Llanowar Visionary +1 M21 189 Hunter's Edge + +# Elves (2) +1 JMP 389 Dwynen's Elite +1 JMP 430 Silhana Wayfinder +1 JMP 447 Wren's Run Vanquisher +1 JMP 408 Leaf Gilder +1 JMP 444 Wildheart Invoker +1 JMP 386 Crushing Canopy +1 M21 210 Titanic Growth +1 JMP 420 Presence of Gond +6 JMP 74 Forest +1 JMP 28 Allosaurus Shepherd +1 JMP 34 Thriving Grove +1 JMP 77 Forest +1 M21 206 Skyway Sniper +1 M21 193 Llanowar Visionary +1 M21 189 Hunter's Edge + +# Feathered Friends (1) +1 JMP 107 Healer's Hawk +1 M21 12 Concordia Pegasus +1 JMP 80 Aerial Assault +1 JMP 99 Dauntless Onslaught +1 JMP 124 Moment of Heroism +1 JMP 133 Sky Tether +6 JMP 45 Plains +1 JMP 5 Steel-Plume Marshal +1 JMP 35 Thriving Heath +1 JMP 44 Plains +1 M21 5 Aven Gagglemaster +1 M21 18 Falconer Adept +1 M21 44 Warded Battlements +1 M21 20 Gale Swooper +1 M21 11 Celestial Enforcer + +# Feathered Friends (2) +1 M21 12 Concordia Pegasus +1 JMP 107 Healer's Hawk +1 JMP 135 Tandem Tactics +1 JMP 133 Sky Tether +6 JMP 45 Plains +1 JMP 5 Steel-Plume Marshal +1 JMP 35 Thriving Heath +1 JMP 44 Plains +1 M21 3 Angelic Ascension +1 M21 5 Aven Gagglemaster +1 M21 18 Falconer Adept +1 M21 44 Warded Battlements +1 M21 40 Swift Response +1 M21 20 Gale Swooper +1 M21 11 Celestial Enforcer + +# Feathered Friends (3) +1 JMP 85 Angel of the Dire Hour +1 JMP 107 Healer's Hawk +1 M21 12 Concordia Pegasus +1 JMP 99 Dauntless Onslaught +1 JMP 134 Take Heart +1 JMP 133 Sky Tether +6 JMP 45 Plains +1 JMP 35 Thriving Heath +1 JMP 44 Plains +1 M21 5 Aven Gagglemaster +1 M21 18 Falconer Adept +1 M21 44 Warded Battlements +1 M21 40 Swift Response +1 M21 20 Gale Swooper +1 M21 11 Celestial Enforcer + +# Feathered Friends (4) +1 JMP 89 Archon of Justice +1 JMP 90 Archon of Redemption +1 JMP 107 Healer's Hawk +1 M21 12 Concordia Pegasus +1 JMP 80 Aerial Assault +1 JMP 79 Aegis of the Heavens +1 JMP 99 Dauntless Onslaught +1 JMP 133 Sky Tether +6 JMP 45 Plains +1 JMP 35 Thriving Heath +1 JMP 44 Plains +1 M21 18 Falconer Adept +1 M21 44 Warded Battlements +1 M21 20 Gale Swooper +1 M21 11 Celestial Enforcer + +# Garruk +1 JMP 373 Affectionate Indrik +1 JMP 381 Brushstrider +1 JMP 426 Rumbling Baloth +1 JMP 416 Orazca Frillback +1 JMP 402 Hunter's Insight +1 M21 199 Ranger's Guile +7 JMP 74 Forest +1 JMP 34 Thriving Grove +1 M21 305 Garruk, Unleashed +1 M21 185 Garruk's Harbinger +1 M21 308 Garruk's Uprising +1 M21 189 Hunter's Edge +1 M21 306 Garruk's Gorehorn +1 M21 178 Drowsing Tyrannodon + +# Heavily Armored (1) +1 JMP 128 Patron of the Valiant +1 JMP 93 Bulwark Giant +1 JMP 118 Lightwalker +1 M21 26 Makeshift Battalion +1 M21 19 Feat of Resistance +1 JMP 95 Cathars' Crusade +6 JMP 45 Plains +1 JMP 8 Trusty Retriever +1 JMP 35 Thriving Heath +1 JMP 43 Plains +1 M21 41 Tempered Veteran +1 M21 37 Siege Striker +1 M21 10 Basri's Solidarity +1 M21 35 Secure the Scene +1 M21 287 Basri's Acolyte + +# Heavily Armored (2) +1 JMP 128 Patron of the Valiant +1 JMP 93 Bulwark Giant +1 JMP 118 Lightwalker +1 M21 26 Makeshift Battalion +1 JMP 106 Gird for Battle +1 JMP 91 Battlefield Promotion +1 JMP 95 Cathars' Crusade +6 JMP 45 Plains +1 JMP 8 Trusty Retriever +1 JMP 35 Thriving Heath +1 JMP 43 Plains +1 M21 41 Tempered Veteran +1 M21 37 Siege Striker +1 M21 35 Secure the Scene +1 M21 287 Basri's Acolyte + +# Heavily Armored (3) +1 JMP 123 Mikaeus, the Lunarch +1 JMP 128 Patron of the Valiant +1 JMP 93 Bulwark Giant +1 JMP 118 Lightwalker +1 M21 26 Makeshift Battalion +1 JMP 91 Battlefield Promotion +1 JMP 100 Divine Arrow +6 JMP 45 Plains +1 JMP 7 Supply Runners +1 JMP 8 Trusty Retriever +1 JMP 35 Thriving Heath +1 JMP 43 Plains +1 M21 41 Tempered Veteran +1 M21 10 Basri's Solidarity +1 M21 287 Basri's Acolyte + +# Heavily Armored (4) +1 JMP 108 High Sentinels of Arashin +1 JMP 93 Bulwark Giant +1 JMP 118 Lightwalker +1 M21 26 Makeshift Battalion +1 JMP 120 Long Road Home +1 JMP 101 Duelist's Heritage +6 JMP 45 Plains +1 JMP 7 Supply Runners +1 JMP 8 Trusty Retriever +1 JMP 35 Thriving Heath +1 JMP 43 Plains +1 M21 41 Tempered Veteran +1 M21 37 Siege Striker +1 M21 40 Swift Response +1 M21 287 Basri's Acolyte + +# Lands (1) +1 JMP 439 Ulvenwald Hydra +1 JMP 379 Awakener Druid +1 JMP 395 Feral Hydra +1 JMP 394 Fa'adiyah Seer +1 JMP 398 Fertilid +1 JMP 435 Sylvan Ranger +1 JMP 444 Wildheart Invoker +1 M21 177 Cultivate +1 JMP 386 Crushing Canopy +1 JMP 390 Elemental Uprising +1 JMP 448 Zendikar's Roil +1 JMP 440 Vastwood Zendikon +6 JMP 74 Forest +1 JMP 34 Thriving Grove +1 JMP 71 Forest + +# Lands (2) +1 JMP 415 Oracle of Mul Daya +1 JMP 379 Awakener Druid +1 JMP 395 Feral Hydra +1 JMP 446 Woodborn Behemoth +1 JMP 394 Fa'adiyah Seer +1 JMP 398 Fertilid +1 JMP 433 Sporemound +1 JMP 435 Sylvan Ranger +1 JMP 444 Wildheart Invoker +1 M21 177 Cultivate +1 JMP 386 Crushing Canopy +1 JMP 390 Elemental Uprising +6 JMP 74 Forest +1 JMP 34 Thriving Grove +1 JMP 71 Forest + +# Lightning (1) +1 JMP 371 Weaver of Lightning +1 JMP 344 Lightning Elemental +1 JMP 345 Lightning Shrieker +1 JMP 335 Homing Lightning +1 JMP 341 Lightning Axe +1 JMP 342 Lightning Bolt +1 JMP 343 Lightning Diadem +6 JMP 64 Mountain +1 JMP 21 Lightning Phoenix +1 JMP 23 Living Lightning +2 JMP 22 Lightning Visionary +1 JMP 32 Lightning-Core Excavator +1 JMP 33 Thriving Bluff +1 JMP 68 Mountain + +# Lightning (2) +1 JMP 291 Ball Lightning +1 JMP 371 Weaver of Lightning +1 JMP 344 Lightning Elemental +1 JMP 302 Chain Lightning +1 JMP 341 Lightning Axe +1 JMP 359 Riddle of Lightning +1 JMP 343 Lightning Diadem +6 JMP 64 Mountain +1 JMP 21 Lightning Phoenix +1 JMP 23 Living Lightning +2 JMP 22 Lightning Visionary +1 JMP 32 Lightning-Core Excavator +1 JMP 33 Thriving Bluff +1 JMP 68 Mountain + +# Liliana +1 JMP 250 Liliana's Elite +1 JMP 205 Blighted Bat +1 JMP 238 Ghoulraiser +1 JMP 286 Wailing Ghoul +1 JMP 276 Settle the Score +1 JMP 217 Cemetery Recruitment +7 JMP 57 Swamp +1 JMP 37 Thriving Moor +1 M21 297 Liliana, Waker of the Dead +1 M21 110 Liliana's Standard Bearer +1 M21 101 Goremand +1 M21 109 Liliana's Devotee +1 M21 119 Rise Again +1 M21 300 Liliana's Steward + +# Minions (1) +1 JMP 237 Ghoulcaller's Accomplice +1 JMP 212 Bone Picker +1 JMP 226 Dutiful Attendant +1 JMP 277 Shambling Goblin +1 JMP 244 Innocent Blood +6 JMP 57 Swamp +1 JMP 15 Kels, Fight Fixer +1 JMP 16 Nocturnal Feeder +1 JMP 37 Thriving Moor +1 JMP 54 Swamp +1 M21 129 Witch's Cauldron +1 M21 97 Eliminate +1 M21 101 Goremand +1 M21 93 Crypt Lurker +1 M21 126 Village Rites + +# Minions (2) +1 JMP 228 Eternal Taskmaster +1 JMP 224 Drainpipe Vermin +1 JMP 226 Dutiful Attendant +1 JMP 237 Ghoulcaller's Accomplice +1 JMP 213 Bone Splinters +6 JMP 57 Swamp +1 JMP 15 Kels, Fight Fixer +1 JMP 16 Nocturnal Feeder +1 JMP 37 Thriving Moor +1 JMP 54 Swamp +1 M21 129 Witch's Cauldron +1 M21 97 Eliminate +1 M21 101 Goremand +1 M21 93 Crypt Lurker +1 M21 126 Village Rites + +# Minions (3) +1 JMP 226 Dutiful Attendant +1 JMP 224 Drainpipe Vermin +1 JMP 237 Ghoulcaller's Accomplice +1 M21 98 Fetid Imp +1 JMP 248 Launch Party +1 JMP 493 Phyrexian Tower +5 JMP 57 Swamp +1 JMP 16 Nocturnal Feeder +1 JMP 37 Thriving Moor +1 JMP 54 Swamp +1 M21 129 Witch's Cauldron +1 M21 97 Eliminate +1 M21 101 Goremand +1 M21 109 Liliana's Devotee +1 M21 93 Crypt Lurker +1 M21 126 Village Rites + +# Minions (4) +1 JMP 236 Ghoulcaller Gisa +1 JMP 226 Dutiful Attendant +1 JMP 237 Ghoulcaller's Accomplice +1 JMP 277 Shambling Goblin +1 M21 98 Fetid Imp +1 JMP 244 Innocent Blood +6 JMP 57 Swamp +1 JMP 16 Nocturnal Feeder +1 JMP 37 Thriving Moor +1 JMP 54 Swamp +1 M21 129 Witch's Cauldron +1 M21 97 Eliminate +1 M21 101 Goremand +1 M21 109 Liliana's Devotee +1 M21 126 Village Rites + +# Phyrexian +1 JMP 278 Sheoldred, Whispering One +1 JMP 227 Entomber Exarch +1 JMP 265 Phyrexian Gargantua +1 JMP 475 Myr Sire +1 JMP 476 Perilous Myr +1 JMP 263 Phyrexian Broodlings +1 JMP 264 Phyrexian Debaser +1 JMP 266 Phyrexian Rager +1 JMP 223 Douse in Gloom +1 JMP 267 Phyrexian Reclamation +1 JMP 262 Parasitic Implant +6 JMP 57 Swamp +1 JMP 37 Thriving Moor +1 JMP 58 Swamp +1 M21 112 Malefic Scythe + +# Pirates (1) +1 JMP 170 Rishadan Airship +1 JMP 149 Departed Deckhand +1 JMP 178 Spectral Sailor +1 JMP 155 Kitesail Corsair +1 JMP 165 Prosperous Pirates +1 JMP 172 Sailor of Means +1 JMP 142 Chart a Course +1 JMP 189 Voyage's End +1 JMP 477 Pirate's Cutlass +1 JMP 144 Coastal Piracy +1 JMP 192 Waterknot +6 JMP 50 Island +1 JMP 11 Corsair Captain +1 JMP 36 Thriving Isle +1 JMP 52 Island + +# Pirates (2) +1 JMP 149 Departed Deckhand +1 JMP 178 Spectral Sailor +1 JMP 155 Kitesail Corsair +1 JMP 165 Prosperous Pirates +1 JMP 170 Rishadan Airship +1 JMP 172 Sailor of Means +1 JMP 142 Chart a Course +1 JMP 477 Pirate's Cutlass +1 JMP 148 Curious Obsession +1 M21 47 Capture Sphere +6 JMP 50 Island +1 JMP 11 Corsair Captain +1 JMP 36 Thriving Isle +1 JMP 52 Island +1 M21 62 Read the Tides + +# Predatory (1) +1 JMP 431 Somberwald Stag +1 JMP 434 Sylvan Brushstrider +1 JMP 380 Brindle Shoat +1 JMP 387 Dawntreader Elk +1 JMP 406 Irresistible Prey +1 JMP 438 Time to Feed +1 JMP 386 Crushing Canopy +1 JMP 473 Marauder's Axe +6 JMP 74 Forest +1 JMP 30 Neyith of the Dire Hunt +1 JMP 34 Thriving Grove +1 JMP 76 Forest +1 M21 182 Fungal Rebirth +1 M21 212 Trufflesnout +1 M21 202 Sabertooth Mauler + +# Predatory (2) +1 JMP 466 Gingerbrute +1 JMP 373 Affectionate Indrik +1 JMP 380 Brindle Shoat +1 JMP 387 Dawntreader Elk +1 JMP 406 Irresistible Prey +1 JMP 438 Time to Feed +1 JMP 386 Crushing Canopy +1 JMP 473 Marauder's Axe +6 JMP 74 Forest +1 JMP 30 Neyith of the Dire Hunt +1 JMP 34 Thriving Grove +1 JMP 76 Forest +1 M21 182 Fungal Rebirth +1 M21 212 Trufflesnout +1 M21 202 Sabertooth Mauler + +# Predatory (3) +1 JMP 436 Thragtusk +1 JMP 431 Somberwald Stag +1 JMP 380 Brindle Shoat +1 JMP 387 Dawntreader Elk +1 JMP 466 Gingerbrute +1 JMP 406 Irresistible Prey +1 JMP 438 Time to Feed +1 JMP 386 Crushing Canopy +1 JMP 473 Marauder's Axe +6 JMP 74 Forest +1 JMP 34 Thriving Grove +1 JMP 76 Forest +1 M21 182 Fungal Rebirth +1 M21 212 Trufflesnout +1 M21 202 Sabertooth Mauler + +# Predatory (4) +1 JMP 424 Ravenous Baloth +1 JMP 373 Affectionate Indrik +1 JMP 380 Brindle Shoat +1 JMP 387 Dawntreader Elk +1 JMP 434 Sylvan Brushstrider +1 JMP 406 Irresistible Prey +1 JMP 438 Time to Feed +1 JMP 411 Momentous Fall +1 JMP 386 Crushing Canopy +6 JMP 74 Forest +1 JMP 34 Thriving Grove +1 JMP 76 Forest +1 M21 182 Fungal Rebirth +1 M21 212 Trufflesnout +1 M21 202 Sabertooth Mauler + +# Rainbow +1 JMP 454 Maelstrom Archangel +1 JMP 461 Chamber Sentry +1 JMP 450 Dinrova Horror +1 JMP 451 Fusion Elemental +1 JMP 455 Raging Regisaur +1 JMP 452 Ironroot Warlord +1 JMP 457 Alloy Myr +1 JMP 486 Skittering Surveyor +1 M21 235 Prismite +1 JMP 449 Auger Spree +1 JMP 478 Prophetic Prism +1 JMP 453 Lawmage's Binding +1 JMP 492 Mirrodin's Core +1 JMP 495 Rupture Spire +1 JMP 74 Forest +1 JMP 50 Island +1 JMP 64 Mountain +1 JMP 45 Plains +1 JMP 57 Swamp +1 JMP 78 Terramorphic Expanse + +# Reanimated (1) +1 JMP 257 Mire Triton +1 JMP 215 Cadaver Imp +1 JMP 221 Crow of Dark Tidings +1 JMP 256 Miasmic Mummy +1 JMP 284 Tithebearer Giant +1 JMP 270 Reanimate +1 JMP 235 Funeral Rites +6 JMP 57 Swamp +1 JMP 37 Thriving Moor +1 JMP 56 Swamp +1 M21 88 Archfiend's Vessel +1 M21 101 Goremand +1 M21 97 Eliminate +1 M21 93 Crypt Lurker +1 M21 119 Rise Again + +# Reanimated (2) +1 JMP 257 Mire Triton +1 JMP 221 Crow of Dark Tidings +1 JMP 256 Miasmic Mummy +1 JMP 270 Reanimate +1 JMP 235 Funeral Rites +1 JMP 252 Macabre Waltz +6 JMP 57 Swamp +1 JMP 37 Thriving Moor +1 JMP 56 Swamp +1 M21 92 Carrion Grub +1 M21 101 Goremand +1 M21 97 Eliminate +1 M21 93 Crypt Lurker +1 M21 119 Rise Again +1 M21 100 Gloom Sower + +# Reanimated (3) +1 JMP 241 Gravewaker +1 JMP 274 Scourge of Nel Toth +1 JMP 257 Mire Triton +1 JMP 221 Crow of Dark Tidings +1 JMP 256 Miasmic Mummy +1 JMP 230 Exhume +1 JMP 235 Funeral Rites +1 JMP 288 Zombie Infestation +6 JMP 57 Swamp +1 JMP 37 Thriving Moor +1 JMP 56 Swamp +1 M21 101 Goremand +1 M21 97 Eliminate +1 M21 93 Crypt Lurker +1 M21 119 Rise Again + +# Reanimated (4) +1 JMP 257 Mire Triton +1 JMP 221 Crow of Dark Tidings +1 JMP 256 Miasmic Mummy +1 JMP 271 Rise of the Dark Realms +1 JMP 235 Funeral Rites +1 JMP 280 Soul Salvage +6 JMP 57 Swamp +1 JMP 37 Thriving Moor +1 JMP 56 Swamp +1 M21 92 Carrion Grub +1 M21 101 Goremand +1 M21 97 Eliminate +1 M21 93 Crypt Lurker +1 M21 119 Rise Again +1 M21 100 Gloom Sower + +# Seismic +1 JMP 331 Grim Lavamancer +1 JMP 304 Cinder Elemental +1 JMP 362 Seismic Elemental +1 JMP 290 Ashmouth Hound +1 JMP 297 Bloodrock Cyclops +1 JMP 351 Molten Ravager +1 JMP 364 Spitting Earth +1 JMP 347 Magmaquake +1 JMP 346 Magma Jet +1 JMP 368 Volcanic Fallout +1 M21 171 Volcanic Geyser +1 JMP 472 Mana Geode +6 JMP 64 Mountain +1 JMP 33 Thriving Bluff +1 JMP 64 Mountain + +# Teferi +1 JMP 164 Prescient Chimera +1 JMP 171 Sage's Row Savant +1 M21 83 Vodalian Arcanist +1 JMP 182 Talrand's Invocation +1 JMP 152 Exclude +1 JMP 156 Leave in the Dust +1 M21 59 Opt +7 JMP 50 Island +1 JMP 36 Thriving Isle +1 M21 290 Teferi, Master of Time +1 M21 76 Teferi's Ageless Insight +1 M21 80 Tolarian Kraken +1 M21 78 Teferi's Tutelage +1 M21 295 Teferi's Protege + +# Tree-Hugging (1) +1 JMP 422 Primordial Sage +1 JMP 442 Wall of Blossoms +1 JMP 375 Ambassador Oak +1 M21 207 Snarespinner +1 JMP 412 Nature's Way +1 JMP 393 Explore +1 JMP 374 Aggressive Urge +1 JMP 386 Crushing Canopy +6 JMP 74 Forest +1 JMP 34 Thriving Grove +1 JMP 70 Forest +1 M21 174 Burlfist Oak +1 M21 213 Warden of the Woods +1 M21 193 Llanowar Visionary +1 M21 187 Gnarled Sage + +# Tree-Hugging (2) +1 JMP 422 Primordial Sage +1 JMP 442 Wall of Blossoms +1 JMP 375 Ambassador Oak +1 M21 207 Snarespinner +1 JMP 412 Nature's Way +1 JMP 393 Explore +1 JMP 386 Crushing Canopy +1 JMP 445 Wildsize +6 JMP 74 Forest +1 JMP 34 Thriving Grove +1 JMP 70 Forest +1 M21 174 Burlfist Oak +1 M21 213 Warden of the Woods +1 M21 193 Llanowar Visionary +1 M21 187 Gnarled Sage + +# Tree-Hugging (3) +1 JMP 442 Wall of Blossoms +1 JMP 375 Ambassador Oak +1 M21 207 Snarespinner +1 JMP 412 Nature's Way +1 JMP 393 Explore +1 JMP 374 Aggressive Urge +1 JMP 386 Crushing Canopy +6 JMP 74 Forest +1 JMP 34 Thriving Grove +1 JMP 70 Forest +1 M21 376 Jolrael, Mwonvuli Recluse +1 M21 174 Burlfist Oak +1 M21 213 Warden of the Woods +1 M21 193 Llanowar Visionary +1 M21 187 Gnarled Sage + +# Tree-Hugging (4) +1 JMP 432 Soul of the Harvest +1 JMP 442 Wall of Blossoms +1 M21 207 Snarespinner +1 JMP 412 Nature's Way +1 JMP 393 Explore +1 JMP 386 Crushing Canopy +1 JMP 445 Wildsize +1 JMP 441 Verdant Embrace +6 JMP 74 Forest +1 JMP 34 Thriving Grove +1 JMP 70 Forest +1 M21 174 Burlfist Oak +1 M21 213 Warden of the Woods +1 M21 193 Llanowar Visionary +1 M21 187 Gnarled Sage + +# Under the Sea (1) +1 JMP 177 Sigiled Starfish +1 JMP 161 Octoprophet +1 JMP 197 Wishful Merfolk +1 JMP 138 Aegis Turtle +1 JMP 189 Voyage's End +1 JMP 489 Unstable Obelisk +1 JMP 192 Waterknot +7 JMP 50 Island +1 JMP 36 Thriving Isle +1 JMP 46 Island +1 M21 60 Pursued Whale +1 M21 80 Tolarian Kraken +1 M21 84 Waker of Waves +1 M21 67 Rousing Read + +# Under the Sea (2) +1 JMP 146 Cryptic Serpent +1 JMP 177 Sigiled Starfish +1 JMP 161 Octoprophet +1 JMP 197 Wishful Merfolk +1 JMP 138 Aegis Turtle +1 JMP 193 Whelming Wave +1 JMP 180 Sweep Away +1 JMP 489 Unstable Obelisk +1 JMP 192 Waterknot +7 JMP 50 Island +1 JMP 36 Thriving Isle +1 JMP 46 Island +1 M21 60 Pursued Whale +1 M21 84 Waker of Waves + +# Unicorns +1 JMP 112 Inspiring Unicorn +1 JMP 122 Mesa Unicorn +1 JMP 131 Ronom Unicorn +1 JMP 136 Valorous Stance +1 JMP 97 Cloudshift +1 JMP 110 Inspired Charge +1 M21 17 Faith's Fetters +6 JMP 45 Plains +1 JMP 3 Emiel the Blessed +1 JMP 1 Blessed Sanctuary +1 JMP 2 Brightmare +1 JMP 35 Thriving Heath +1 JMP 41 Plains +1 M21 42 Valorous Steed +1 M21 14 Daybreak Charger + +# Vampires (1) +1 JMP 206 Blood Artist +1 JMP 209 Bloodbond Vampire +1 JMP 239 Gifted Aetherborn +1 JMP 245 Kalastria Nightwatch +1 JMP 199 Agonizing Syphon +1 JMP 247 Last Gasp +1 JMP 229 Eternal Thirst +6 JMP 57 Swamp +1 JMP 16 Nocturnal Feeder +1 JMP 37 Thriving Moor +1 JMP 60 Swamp +1 M21 362 Vito, Thorn of the Dusk Rose +1 M21 122 Silversmote Ghoul +1 M21 121 Sanguine Indulgence +1 M21 100 Gloom Sower + +# Vampires (2) +1 JMP 208 Blood Host +1 JMP 209 Bloodbond Vampire +1 JMP 239 Gifted Aetherborn +1 JMP 218 Child of Night +1 JMP 199 Agonizing Syphon +1 JMP 247 Last Gasp +1 JMP 229 Eternal Thirst +6 JMP 57 Swamp +1 JMP 16 Nocturnal Feeder +1 JMP 37 Thriving Moor +1 JMP 60 Swamp +1 M21 362 Vito, Thorn of the Dusk Rose +1 M21 122 Silversmote Ghoul +1 M21 121 Sanguine Indulgence +1 M21 100 Gloom Sower + +# Vampires (3) +1 JMP 272 Sangromancer +1 JMP 209 Bloodbond Vampire +1 JMP 239 Gifted Aetherborn +1 JMP 275 Sengir Vampire +1 JMP 285 Vampire Neonate +1 JMP 199 Agonizing Syphon +1 JMP 247 Last Gasp +1 JMP 231 Exquisite Blood +6 JMP 57 Swamp +1 JMP 16 Nocturnal Feeder +1 JMP 37 Thriving Moor +1 JMP 60 Swamp +1 M21 122 Silversmote Ghoul +1 M21 121 Sanguine Indulgence +1 M21 100 Gloom Sower + +# Vampires (4) +1 JMP 225 Drana, Liberator of Malakir +1 JMP 209 Bloodbond Vampire +1 JMP 232 Falkenrath Noble +1 JMP 239 Gifted Aetherborn +1 JMP 218 Child of Night +1 JMP 199 Agonizing Syphon +1 JMP 247 Last Gasp +1 JMP 254 Mark of the Vampire +6 JMP 57 Swamp +1 JMP 16 Nocturnal Feeder +1 JMP 37 Thriving Moor +1 JMP 60 Swamp +1 M21 122 Silversmote Ghoul +1 M21 121 Sanguine Indulgence +1 M21 100 Gloom Sower + +# Walls +1 JMP 442 Wall of Blossoms +1 JMP 382 Carven Caryatid +1 JMP 465 Gargoyle Sentinel +1 JMP 417 Overgrown Battlement +1 JMP 401 Grave Bramble +1 JMP 443 Wall of Vines +1 JMP 480 Roving Keep +1 M21 195 Portcullis Vine +1 JMP 386 Crushing Canopy +1 JMP 490 Warmonger's Chariot +1 JMP 378 Assault Formation +6 JMP 74 Forest +1 JMP 31 Towering Titan +1 JMP 34 Thriving Grove +1 JMP 75 Forest + +# Well-Read (1) +1 JMP 487 Suspicious Bookcase +1 JMP 162 Oneirophage +1 JMP 150 Erratic Visionary +1 JMP 481 Runed Servitor +1 M21 59 Opt +1 JMP 147 Curiosity +1 JMP 159 Narcolepsy +6 JMP 50 Island +1 JMP 13 Ormos, Archive Keeper +1 JMP 36 Thriving Isle +1 JMP 53 Island +1 M21 80 Tolarian Kraken +1 M21 81 Tome Anima +1 M21 55 Library Larcenist +1 M21 67 Rousing Read + +# Well-Read (2) +1 JMP 162 Oneirophage +1 JMP 150 Erratic Visionary +1 JMP 481 Runed Servitor +1 M21 59 Opt +1 JMP 459 Arcane Encyclopedia +1 JMP 147 Curiosity +1 M21 47 Capture Sphere +6 JMP 50 Island +1 JMP 13 Ormos, Archive Keeper +1 JMP 36 Thriving Isle +1 JMP 53 Island +1 M21 80 Tolarian Kraken +1 M21 81 Tome Anima +1 M21 55 Library Larcenist +1 M21 67 Rousing Read + +# Well-Read (3) +1 JMP 162 Oneirophage +1 JMP 487 Suspicious Bookcase +1 JMP 143 Cloudreader Sphinx +1 JMP 150 Erratic Visionary +1 JMP 481 Runed Servitor +1 M21 59 Opt +1 JMP 169 Rhystic Study +1 JMP 147 Curiosity +1 JMP 159 Narcolepsy +6 JMP 50 Island +1 JMP 36 Thriving Isle +1 JMP 53 Island +1 M21 80 Tolarian Kraken +1 M21 81 Tome Anima +1 M21 55 Library Larcenist + +# Well-Read (4) +1 JMP 158 Mystic Archaeologist +1 JMP 162 Oneirophage +1 JMP 487 Suspicious Bookcase +1 JMP 143 Cloudreader Sphinx +1 JMP 481 Runed Servitor +1 JMP 167 Read the Runes +1 M21 59 Opt +1 JMP 147 Curiosity +1 M21 47 Capture Sphere +6 JMP 50 Island +1 JMP 36 Thriving Isle +1 JMP 53 Island +1 M21 80 Tolarian Kraken +1 M21 81 Tome Anima +1 M21 55 Library Larcenist + +# Witchcraft (1) +1 JMP 211 Bogbrew Witch +1 JMP 253 Malakir Familiar +1 JMP 283 Tempting Witch +1 JMP 210 Bloodhunter Bat +1 JMP 216 Cauldron Familiar +2 JMP 234 Festering Newt +1 JMP 201 Bake into a Pie +1 JMP 460 Bubbling Cauldron +6 JMP 57 Swamp +1 JMP 18 Witch of the Moors +1 JMP 37 Thriving Moor +1 JMP 59 Swamp +1 M21 129 Witch's Cauldron +1 M21 99 Finishing Blow + +# Witchcraft (2) +1 JMP 283 Tempting Witch +1 JMP 211 Bogbrew Witch +1 JMP 282 Swarm of Bloodflies +1 JMP 203 Black Cat +1 JMP 210 Bloodhunter Bat +2 JMP 234 Festering Newt +1 JMP 207 Blood Divination +1 JMP 201 Bake into a Pie +1 JMP 247 Last Gasp +1 JMP 460 Bubbling Cauldron +6 JMP 57 Swamp +1 JMP 18 Witch of the Moors +1 JMP 37 Thriving Moor +1 JMP 59 Swamp + +# Wizards (1) +1 JMP 181 Talrand, Sky Summoner +1 JMP 153 Exclusion Mage +1 JMP 171 Sage's Row Savant +1 M21 83 Vodalian Arcanist +1 JMP 182 Talrand's Invocation +1 JMP 196 Winged Words +1 JMP 198 Wizard's Retort +1 JMP 140 Befuddle +1 M21 51 Frost Breath +1 M21 59 Opt +6 JMP 50 Island +1 JMP 36 Thriving Isle +1 JMP 47 Island +1 M21 71 Shipwreck Dowser +1 M21 62 Read the Tides + +# Wizards (2) +1 JMP 153 Exclusion Mage +1 JMP 145 Crookclaw Transmuter +1 JMP 171 Sage's Row Savant +1 JMP 173 Sea Gate Oracle +1 M21 83 Vodalian Arcanist +1 JMP 182 Talrand's Invocation +1 JMP 196 Winged Words +1 JMP 198 Wizard's Retort +1 M21 59 Opt +1 JMP 494 Riptide Laboratory +5 JMP 50 Island +1 JMP 36 Thriving Isle +1 JMP 47 Island +1 M21 71 Shipwreck Dowser +1 M21 66 Rookie Mistake +1 M21 62 Read the Tides + +# Wizards (3) +1 JMP 181 Talrand, Sky Summoner +1 JMP 171 Sage's Row Savant +1 M21 83 Vodalian Arcanist +1 JMP 182 Talrand's Invocation +1 JMP 196 Winged Words +1 JMP 198 Wizard's Retort +1 M21 82 Unsubstantiate +1 M21 59 Opt +1 M21 47 Capture Sphere +6 JMP 50 Island +1 JMP 36 Thriving Isle +1 JMP 47 Island +1 M21 71 Shipwreck Dowser +1 M21 62 Read the Tides +1 M21 295 Teferi's Protege + +# Wizards (4) +1 JMP 171 Sage's Row Savant +1 JMP 179 Storm Sculptor +1 M21 83 Vodalian Arcanist +1 JMP 182 Talrand's Invocation +1 JMP 198 Wizard's Retort +1 JMP 163 Peel from Reality +1 M21 61 Rain of Revelation +1 M21 51 Frost Breath +6 JMP 50 Island +1 JMP 36 Thriving Isle +1 JMP 47 Island +1 M21 348 Barrin, Tolarian Archmage +1 M21 71 Shipwreck Dowser +1 M21 62 Read the Tides +1 M21 295 Teferi's Protege